# Schema reference, draft 0.1

Every type of OFAP draft 0.1, generated from [`schema.ts`](https://github.com/OFA-Foundation/open-financial-agent-protocol/blob/main/schema.ts) through [`schema.json`](https://openfinancialagent.org/schemas/0.1/schema.json). The sections follow `schema.ts`. The rules in the descriptions use the RFC 2119 keywords.

## 1. Identity of the extension

### `ConformanceLevel`

Conformance level that a distributor declares.

- 1 Inform: `get_distributor`, `list_products`, `get_documents`
- 2 Quote: level 1, plus `get_question_set` and `quote`
- 3 Subscribe: level 2, plus `submit_application` or `conclude`
- 4 Service: level 3, plus `get_agreement` and `amend_agreement`

A server that declares a level MUST implement every tool of that level and of the levels below it, except that level 3 needs only one of `submit_application` and `conclude`.

Type: one of `1`, `2`, `3`, `4`.

### `InterfaceKind`

How a distributor exposes the protocol.

Type: one of `tools`, `conversation`.

### `OfapSettings`

Settings object of the extension. A server puts it under `capabilities.extensions["org.openfinancialagent/ofap"]` in its MCP `server/discover` response, and under the same key in the `extensions` field of its PAP `poppy.json`.

| Field | Type | Required | Description |
|---|---|---|---|
| `version` | string | Yes | Protocol version the server implements. |
| `conformance_level` | [`ConformanceLevel`](https://openfinancialagent.org/spec/0.1/reference#ConformanceLevel) | Yes |  |
| `interfaces` | list of [`InterfaceKind`](https://openfinancialagent.org/spec/0.1/reference#InterfaceKind) | Yes |  |
| `sectors` | list of [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes | Sector modules the server implements. |
| `lines` | list of [`ProductLine`](https://openfinancialagent.org/spec/0.1/reference#ProductLine) | Yes | Product lines the server can quote or serve. |
| `countries` | list of [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) | Yes | Countries where the server's products are sold. |
| `can_conclude` | boolean | Yes | True when the server can conclude an agreement inside the agent (`conclude`). False when the customer always finishes on the distributor's own page. |

## 2. Common types

### `CountryCode`

ISO 3166-1 alpha-2 country code, upper case.

Type: string. Pattern: `^[A-Z]{2}$`.

### `CurrencyCode`

ISO 4217 currency code.

Type: string. Pattern: `^[A-Z]{3}$`.

### `LanguageTag`

BCP 47 language tag, for example `fr` or `es-ES`.

Type: string. Pattern: `^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$`.

### `DateString`

Calendar date, ISO 8601.

Type: string (date).

### `DateTimeString`

Date and time with offset, ISO 8601.

Type: string (date-time).

### `Url`

Type: string (uri).

### `Sha256`

Lower-case hex SHA-256 digest.

Type: string. Pattern: `^[a-f0-9]{64}$`.

### `Decimal`

A decimal number as a string, so that no value is lost to floating point, for example "12.90".

Type: string. Pattern: `^-?[0-9]+(\.[0-9]+)?$`.

### `Money`

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | [`Decimal`](https://openfinancialagent.org/spec/0.1/reference#Decimal) | Yes |  |
| `currency` | [`CurrencyCode`](https://openfinancialagent.org/spec/0.1/reference#CurrencyCode) | Yes |  |

### `ProductLine`

Product line. Servers SHOULD use a value of `KnownProductLine`. Other values MUST be lower case with underscores.

Type: string. Pattern: `^[a-z][a-z0-9_]*$`.

### `KnownProductLine`

Lines that have, or will have, a profile in OFAP. A sector module adds its own group.

Type: one of `pet`, `home`, `renters`, `motor`, `travel`, `health`, `life`, `funeral`, `borrower_insurance`, `professional_liability`, `general_liability`, `business_owners_policy`, `workers_compensation`.

## 3. Distributor (core, level 1)

### `DistributorRole`

Role of a party in the distribution chain. The provider roles are in `ProviderRole`. A sector module adds its intermediary roles here.

Type: [`ProviderRole`](https://openfinancialagent.org/spec/0.1/reference#ProviderRole) or `broker` or `agent` or `mga` or `comparator`.

### `RegisterIdentifierType`

Kind of public register identifier. A sector module adds the registers of its sector.

Type: one of `orias_number`, `fca_frn`, `npn`, `dgsfp_key`, `other`.

### `Licence`

One entry in a public register that gives the right to distribute a product.

| Field | Type | Required | Description |
|---|---|---|---|
| `country` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) | Yes |  |
| `regulator` | string | Yes | Supervising authority, for example "ACPR", "FCA", "DGSFP". |
| `register` | string | Yes | Name of the public register, for example "ORIAS", "FCA Register", "NIPR". |
| `identifier_type` | [`RegisterIdentifierType`](https://openfinancialagent.org/spec/0.1/reference#RegisterIdentifierType) | Yes |  |
| `identifier` | string | Yes |  |
| `role` | [`DistributorRole`](https://openfinancialagent.org/spec/0.1/reference#DistributorRole) | Yes |  |
| `sectors` | list of [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes | Sectors this entry covers. |
| `categories` | list of string |  | Register categories, for example ORIAS "COA" (insurance broker) or "AGA" (tied insurance agent). |
| `subdivisions` | list of string |  | Subdivisions where the licence applies, for example US states as ISO 3166-2 codes ("US-CA"). Omit when the licence covers the whole country. |
| `verification_url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  | Public page where anyone can check the entry. |

### `GetDistributorParams`

Params of `get_distributor`. No params.

Type: object.

### `GetDistributorResult`

| Field | Type | Required | Description |
|---|---|---|---|
| `legal_name` | string | Yes |  |
| `trading_name` | string |  |  |
| `domain` | string | Yes | Domain the distributor controls. It MUST match the server's own domain. |
| `roles` | list of [`DistributorRole`](https://openfinancialagent.org/spec/0.1/reference#DistributorRole) | Yes |  |
| `licences` | list of [`Licence`](https://openfinancialagent.org/spec/0.1/reference#Licence) | Yes | Every licence the distributor relies on to sell the listed products. |
| `countries` | list of [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) | Yes |  |
| `complaints_url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  | Where a customer can complain. Required by most regulators. |
| `ofap` | [`OfapSettings`](https://openfinancialagent.org/spec/0.1/reference#OfapSettings) | Yes | Settings the server declares for OFAP. |

## 4. Products (core, level 1)

### `ProviderRole`

Role of the party that carries the risk or provides the money. A sector module adds its provider role, for example a lender.

Type: `insurer`.

### `ProviderRef`

The party that carries the risk or provides the money.

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes |  |
| `legal_name` | string |  |  |
| `role` | [`ProviderRole`](https://openfinancialagent.org/spec/0.1/reference#ProviderRole) | Yes |  |
| `licence` | [`Licence`](https://openfinancialagent.org/spec/0.1/reference#Licence) |  |  |

### `SubscriptionPath`

How a customer can subscribe to a product.

Type: one of `conclude`, `application`, `handoff`.

### `Product`

| Field | Type | Required | Description |
|---|---|---|---|
| `product_id` | string | Yes |  |
| `name` | string | Yes |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes |  |
| `line` | [`ProductLine`](https://openfinancialagent.org/spec/0.1/reference#ProductLine) | Yes |  |
| `provider` | [`ProviderRef`](https://openfinancialagent.org/spec/0.1/reference#ProviderRef) |  | Provider of the product, when it is not the distributor. |
| `countries` | list of [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) | Yes |  |
| `summary` | string | Yes |  |
| `details` | [`ProductDetails`](https://openfinancialagent.org/spec/0.1/reference#ProductDetails) | Yes |  |
| `subscription_paths` | list of [`SubscriptionPath`](https://openfinancialagent.org/spec/0.1/reference#SubscriptionPath) | Yes |  |

### `ProductDetails`

Details of the product's sector. One member per sector module.

Type: [`InsuranceProductDetails`](https://openfinancialagent.org/spec/0.1/reference#InsuranceProductDetails).

### `ListProductsParams`

| Field | Type | Required | Description |
|---|---|---|---|
| `country` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) |  |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) |  |  |
| `line` | [`ProductLine`](https://openfinancialagent.org/spec/0.1/reference#ProductLine) |  |  |
| `cursor` | string |  |  |

### `ListProductsResult`

| Field | Type | Required | Description |
|---|---|---|---|
| `products` | list of [`Product`](https://openfinancialagent.org/spec/0.1/reference#Product) | Yes |  |
| `next_cursor` | string |  |  |

## 5. Documents (core, level 1)

### `DocumentType`

Kind of document. A sector module adds the documents that the law of its sector asks for.

Type: one of `pre_contract_information`, `terms_of_business`, `other`, `ipid`, `general_conditions`, `special_conditions`, `demands_and_needs`, `proof_of_insurance`.

### `FinancialDocument`

| Field | Type | Required | Description |
|---|---|---|---|
| `document_id` | string | Yes |  |
| `type` | [`DocumentType`](https://openfinancialagent.org/spec/0.1/reference#DocumentType) | Yes |  |
| `title` | string | Yes |  |
| `language` | [`LanguageTag`](https://openfinancialagent.org/spec/0.1/reference#LanguageTag) | Yes |  |
| `version` | string | Yes | Version label set by the distributor. A new text MUST get a new version. |
| `url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) | Yes |  |
| `media_type` | string | Yes |  |
| `sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes | Digest of the exact file at `url`. Used as evidence of what was delivered. |
| `required_before_commitment` | boolean | Yes | True when the law requires that the customer receives this document before they commit, for example the IPID. |

### `GetDocumentsParams`

Params of `get_documents`. Give exactly one of the three references.

**Variant 1**

| Field | Type | Required | Description |
|---|---|---|---|
| `product_id` | string | Yes |  |
| `country` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) |  |  |
| `language` | [`LanguageTag`](https://openfinancialagent.org/spec/0.1/reference#LanguageTag) |  |  |

**Variant 2**

| Field | Type | Required | Description |
|---|---|---|---|
| `offer_id` | string | Yes |  |
| `language` | [`LanguageTag`](https://openfinancialagent.org/spec/0.1/reference#LanguageTag) |  |  |

**Variant 3**

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |
| `language` | [`LanguageTag`](https://openfinancialagent.org/spec/0.1/reference#LanguageTag) |  |  |

### `GetDocumentsResult`

| Field | Type | Required | Description |
|---|---|---|---|
| `documents` | list of [`FinancialDocument`](https://openfinancialagent.org/spec/0.1/reference#FinancialDocument) | Yes |  |

## 6. Question sets (core, level 2)

### `CollectedBy`

Who collects the value of a question.
- `agent`: the agent MAY ask and send the value.
- `distributor_page`: the value MUST NOT pass through the agent. The customer   gives it on the distributor's own page. Use it for identity numbers, bank   account and card numbers, tax numbers, health details and passwords.

Type: one of `agent`, `distributor_page`.

### `AnswersSchema`

A JSON Schema (2020-12) object that describes the answers. Each property is one question. OFAP adds these keywords to each property:

- `x-collected-by`: a `CollectedBy` value. Default `agent`.
- `x-profile-key`: the key of the matching question in the line profile,   when the question is part of the common core.
- `x-ask`: a short question the agent can ask the customer, in plain words.

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `object` | Yes |  |
| `properties` | map of map of any | Yes |  |
| `required` | list of string |  |  |

### `QuestionSet`

| Field | Type | Required | Description |
|---|---|---|---|
| `question_set_id` | string | Yes |  |
| `version` | string | Yes | Version of this question set. A change in any question MUST change it. |
| `product_id` | string | Yes |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes |  |
| `line` | [`ProductLine`](https://openfinancialagent.org/spec/0.1/reference#ProductLine) | Yes |  |
| `profile` | string |  | Line profile and version this question set builds on, for example "insurance/pet@0.1". |
| `answers_schema` | [`AnswersSchema`](https://openfinancialagent.org/spec/0.1/reference#AnswersSchema) | Yes |  |

### `GetQuestionSetParams`

| Field | Type | Required | Description |
|---|---|---|---|
| `product_id` | string | Yes |  |
| `country` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) |  |  |

### `GetQuestionSetResult`

| Field | Type | Required | Description |
|---|---|---|---|
| `question_set` | [`QuestionSet`](https://openfinancialagent.org/spec/0.1/reference#QuestionSet) | Yes |  |

### `QuestionSetRef`

| Field | Type | Required | Description |
|---|---|---|---|
| `question_set_id` | string | Yes |  |
| `version` | string | Yes |  |

### `Answers`

Answers keyed by property name in `answers_schema`.

Type: map of any.

## 7. Quote (core, level 2)

### `QuoteParams`

Params of `quote`.

The agent MAY send partial answers. The server then returns `needs_input` with the missing fields, and the agent calls `quote` again with more answers. This one loop replaces the multi-step flow tools (`action`, `stateUpdates`, `sessionId`) that most servers use. Some servers already work this way.

To read a quote that is still in progress, send only `quote_request_id`.

**Variant 1**

| Field | Type | Required | Description |
|---|---|---|---|
| `product_id` | string | Yes |  |
| `question_set` | [`QuestionSetRef`](https://openfinancialagent.org/spec/0.1/reference#QuestionSetRef) | Yes |  |
| `answers` | [`Answers`](https://openfinancialagent.org/spec/0.1/reference#Answers) | Yes |  |
| `options` | list of string |  | Options the customer wants priced, by key (optional covers, extra services). |
| `language` | [`LanguageTag`](https://openfinancialagent.org/spec/0.1/reference#LanguageTag) |  |  |

**Variant 2**

| Field | Type | Required | Description |
|---|---|---|---|
| `quote_request_id` | string | Yes |  |

### `Offer`

One priced offer. A distributor can return several offers: one per tier, per provider, or per duration.

| Field | Type | Required | Description |
|---|---|---|---|
| `offer_id` | string | Yes |  |
| `product_id` | string | Yes |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes |  |
| `tier` | string |  | Name of the tier or plan, for example "Terceros Ampliado" or "Band B". |
| `provider` | [`ProviderRef`](https://openfinancialagent.org/spec/0.1/reference#ProviderRef) | Yes |  |
| `pricing` | [`Pricing`](https://openfinancialagent.org/spec/0.1/reference#Pricing) | Yes |  |
| `indicative` | boolean | Yes | True when the offer is indicative and can change after checks. |
| `valid_until` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) | Yes | Last moment the offer can be accepted at this price. |
| `subscription_paths` | list of [`SubscriptionPath`](https://openfinancialagent.org/spec/0.1/reference#SubscriptionPath) | Yes |  |
| `handoff_url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  | Page where the customer can continue with this exact offer. Required when `subscription_paths` contains `handoff`. It MUST NOT contain personal data. |
| `recommended` | boolean |  | True when the distributor recommends this offer after advice. |

### `Pricing`

Price in the form of the offer's sector. One member per sector module.

Type: [`InsurancePricing`](https://openfinancialagent.org/spec/0.1/reference#InsurancePricing).

### `QuoteResult`

**`status: complete`**

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | `complete` | Yes |  |
| `quote_request_id` | string | Yes |  |
| `question_set` | [`QuestionSetRef`](https://openfinancialagent.org/spec/0.1/reference#QuestionSetRef) | Yes |  |
| `answers_sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes | SHA-256 of the canonical JSON (RFC 8785) of the answers used. |
| `offers` | list of [`Offer`](https://openfinancialagent.org/spec/0.1/reference#Offer) | Yes |  |
| `no_offer` | [`NoOffer`](https://openfinancialagent.org/spec/0.1/reference#NoOffer) |  | Required when `offers` is empty. |

**`status: needs_input`**

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | `needs_input` | Yes |  |
| `quote_request_id` | string | Yes |  |
| `missing_fields` | list of [`FieldIssue`](https://openfinancialagent.org/spec/0.1/reference#FieldIssue) | Yes | Properties of `answers_schema` that are missing or not valid. |

**`status: in_progress`**

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | `in_progress` | Yes |  |
| `quote_request_id` | string | Yes |  |
| `retry_after_seconds` | number | Yes | When the agent SHOULD read the quote again. |
| `message` | string |  | Plain-language status, for example "Two insurers have answered, three to go". |

### `NoOffer`

Why no offer is possible.

| Field | Type | Required | Description |
|---|---|---|---|
| `reason_code` | string | Yes | Stable reason code, for example "risk_not_accepted", "age_not_accepted", "breed_not_accepted", "claims_history_not_accepted", "not_available_in_region". |
| `message` | string | Yes | Plain-language explanation the agent can pass to the customer. |

### `FieldIssue`

| Field | Type | Required | Description |
|---|---|---|---|
| `field` | string | Yes |  |
| `issue` | one of `missing`, `invalid`, `not_accepted` | Yes |  |
| `message` | string |  |  |

## 8. Sector modules

### `Sector`

Sector modules of this draft. Draft 0.1 defines one module: insurance (section 9).

The core is the same for every sector. A sector module adds what the law of its sector asks for. A module MUST define:
- its value in `Sector`, and its group of lines in `KnownProductLine`;
- one member of `ProductDetails`, `Pricing` and `NeedsAssessment`, each with   a `sector` field that holds the module's value;
- its values in `ProviderRole`, `DistributorRole`, `RegisterIdentifierType`,   `DocumentType`, `PartyRole` and `AmendmentChange`, when the sector needs   them;
- which of its documents are `required_before_commitment`;
- its line profiles in `profiles.ts`, and checks for the rules of its sector   that a JSON Schema cannot express.

A module MUST NOT change a core type or a core rule. The tools, the quote loop, the declaration, the PAP operations and the evidence record stay the same for every sector.

A server declares the modules it implements in `OfapSettings.sectors`. An agent MUST NOT show a product, an offer or an agreement of a sector it does not implement as if it were of a known sector.

Candidate modules for a later draft:
- credit: SECCI and ESIS, creditworthiness (CCD Art. 18, MCD Art. 18), APR   and total cost of credit;
- investment: KID (PRIIPs), suitability or appropriateness (MiFID II Art.   25), fees and risk indicator.

Type: `insurance`.

## 9. Insurance module

### `PremiumPeriod`

Type: one of `month`, `quarter`, `half_year`, `year`, `single`.

### `Premium`

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) | Yes |  |
| `period` | [`PremiumPeriod`](https://openfinancialagent.org/spec/0.1/reference#PremiumPeriod) | Yes |  |
| `taxes_included` | boolean | Yes | True when taxes and mandatory levies are in `amount`. |

### `CoverLimit`

| Field | Type | Required | Description |
|---|---|---|---|
| `key` | string | Yes | Stable key, for example "vet_costs_per_year". |
| `label` | string | Yes |  |
| `limit` | [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) |  |  |
| `reimbursement_rate` | number |  | Share of a claim paid back, from 0 to 1. Used by pet and health cover. |

### `CoverSummary`

| Field | Type | Required | Description |
|---|---|---|---|
| `included` | list of string | Yes | Covers included, as short labels. |
| `limits` | list of [`CoverLimit`](https://openfinancialagent.org/spec/0.1/reference#CoverLimit) |  |  |
| `excess` | [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) |  |  |
| `main_exclusions` | list of string |  | Main exclusions. The full list is in the documents. |
| `waiting_period_days` | number |  |  |

### `ProductOption`

An option the customer can add (optional cover, extra service).

| Field | Type | Required | Description |
|---|---|---|---|
| `key` | string | Yes |  |
| `label` | string | Yes |  |
| `price` | [`Premium`](https://openfinancialagent.org/spec/0.1/reference#Premium) or [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) |  | Cost of the option, in the same form as the main price. |

### `InsuranceProductDetails`

| Field | Type | Required | Description |
|---|---|---|---|
| `sector` | `insurance` | Yes |  |
| `cover` | [`CoverSummary`](https://openfinancialagent.org/spec/0.1/reference#CoverSummary) | Yes |  |
| `options` | list of [`ProductOption`](https://openfinancialagent.org/spec/0.1/reference#ProductOption) |  |  |

### `InsurancePricing`

| Field | Type | Required | Description |
|---|---|---|---|
| `sector` | `insurance` | Yes |  |
| `premium` | [`Premium`](https://openfinancialagent.org/spec/0.1/reference#Premium) | Yes |  |
| `alternative_premiums` | list of [`Premium`](https://openfinancialagent.org/spec/0.1/reference#Premium) |  | The same price for another period, when the server can give it. |
| `cover` | [`CoverSummary`](https://openfinancialagent.org/spec/0.1/reference#CoverSummary) | Yes |  |
| `options` | list of [`ProductOption`](https://openfinancialagent.org/spec/0.1/reference#ProductOption) |  |  |
| `start_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |

### `InsuranceNeedsAssessment`

Demands and needs, recorded before the contract is concluded (IDD Art. 20(1)).

| Field | Type | Required | Description |
|---|---|---|---|
| `sector` | `insurance` | Yes |  |
| `kind` | `demands_and_needs` | Yes |  |
| `statement` | string | Yes | The need in the customer's words or a faithful summary. |
| `advice_given` | boolean | Yes | True when the distributor gave a personal recommendation (advice). |
| `advice_rationale` | string |  | Why the recommended offer meets the need. Required when `advice_given` is true. |
| `recorded_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) | Yes |  |

## 10. Parties, needs and consent (core)

### `PartyRole`

Role of a party in the agreement. A sector module adds the roles of its sector.

Type: one of `subscriber`, `payer`, `insured`, `beneficiary`.

### `Party`

A person or organisation in the agreement. Data in a party MUST be limited to what the product needs. Identity numbers MUST NOT be here (see `CollectedBy`).

| Field | Type | Required | Description |
|---|---|---|---|
| `party_ref` | string | Yes | Local reference, unique in one application. |
| `kind` | one of `person`, `organisation` | Yes |  |
| `roles` | list of [`PartyRole`](https://openfinancialagent.org/spec/0.1/reference#PartyRole) | Yes |  |
| `display_name` | string | Yes |  |
| `relationship_to_subscriber` | string |  | Relationship to the subscriber, for example "self", "spouse", "child", "employee". |
| `date_of_birth` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |
| `country_of_residence` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) |  |  |

### `NeedsAssessment`

Needs assessment that the law of the sector asks for. One member per sector module.

Type: [`InsuranceNeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#InsuranceNeedsAssessment).

### `ContactConsent`

The customer's choice about later contact.

| Field | Type | Required | Description |
|---|---|---|---|
| `channels` | list of one of `email`, `phone`, `sms`, `post` | Yes | Channels the distributor MAY use to contact the customer. Empty means none. |
| `details_collected_by` | [`CollectedBy`](https://openfinancialagent.org/spec/0.1/reference#CollectedBy) | Yes | When `distributor_page`, the contact details never pass through the agent. |
| `providers_may_contact` | boolean | Yes | True only when the customer agreed that providers may contact them too. |
| `purposes` | list of string | Yes | Purposes the customer agreed to, for example "this_application", "marketing". |

### `DeliveredDocument`

| Field | Type | Required | Description |
|---|---|---|---|
| `document_id` | string | Yes |  |
| `version` | string | Yes |  |
| `sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes |  |
| `delivered_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) | Yes |  |

### `Declaration`

The answers the customer commits to.

| Field | Type | Required | Description |
|---|---|---|---|
| `question_set` | [`QuestionSetRef`](https://openfinancialagent.org/spec/0.1/reference#QuestionSetRef) | Yes |  |
| `answers` | [`Answers`](https://openfinancialagent.org/spec/0.1/reference#Answers) | Yes |  |
| `answers_sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes |  |
| `accuracy_confirmed` | boolean | Yes | True when the customer confirmed that the answers are true and complete. The agent MUST NOT set it to true for invented or test data. |

## 11. PAP operations with financial terms (levels 3 and 4)

### `OperationState`

Type: one of `proposed`, `in_progress`, `succeeded`, `failed`, `cancelled`, `expired`.

### `Operation<(ApplicationTerms|AmendmentTerms)>`

A PAP operation. This mirrors the PAP operations extension (version 1). PAP is the authority for every field here except `terms` and `ofap_progress`. When the two differ, PAP wins.

An MCP tool returns it in `structuredContent.operation`. The agent confirms it at the PAP operations endpoint, never through an OFAP tool.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation_id` | string | Yes |  |
| `revision` | number | Yes | Starts at 1. Increases when the terms change. Only the latest revision can be confirmed. |
| `state` | [`OperationState`](https://openfinancialagent.org/spec/0.1/reference#OperationState) | Yes |  |
| `summary` | string | Yes | Plain-language terms that the customer can decide from alone. |
| `terms` | [`ApplicationTerms`](https://openfinancialagent.org/spec/0.1/reference#ApplicationTerms) or [`AmendmentTerms`](https://openfinancialagent.org/spec/0.1/reference#AmendmentTerms) | Yes |  |
| `expires_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) |  | Required while `proposed`. |
| `user_approval_required` | boolean | Yes | When true, a standing permission is not enough. The customer MUST approve this revision. |
| `url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  |  |
| `confirmation` | null or object with `revision`, `approved_by`, `confirmed_at` | Yes |  |
| `result` | null or object with `summary`, `data` | Yes |  |
| `ofap_progress` | [`OfapProgress`](https://openfinancialagent.org/spec/0.1/reference#OfapProgress) |  | OFAP reason why the operation is `in_progress`. Not a PAP field. Clients that do not know OFAP ignore it. |

### `Operation<AmendmentTerms>`

A PAP operation. This mirrors the PAP operations extension (version 1). PAP is the authority for every field here except `terms` and `ofap_progress`. When the two differ, PAP wins.

An MCP tool returns it in `structuredContent.operation`. The agent confirms it at the PAP operations endpoint, never through an OFAP tool.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation_id` | string | Yes |  |
| `revision` | number | Yes | Starts at 1. Increases when the terms change. Only the latest revision can be confirmed. |
| `state` | [`OperationState`](https://openfinancialagent.org/spec/0.1/reference#OperationState) | Yes |  |
| `summary` | string | Yes | Plain-language terms that the customer can decide from alone. |
| `terms` | [`AmendmentTerms`](https://openfinancialagent.org/spec/0.1/reference#AmendmentTerms) | Yes |  |
| `expires_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) |  | Required while `proposed`. |
| `user_approval_required` | boolean | Yes | When true, a standing permission is not enough. The customer MUST approve this revision. |
| `url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  |  |
| `confirmation` | null or object with `revision`, `approved_by`, `confirmed_at` | Yes |  |
| `result` | null or object with `summary`, `data` | Yes |  |
| `ofap_progress` | [`OfapProgress`](https://openfinancialagent.org/spec/0.1/reference#OfapProgress) |  | OFAP reason why the operation is `in_progress`. Not a PAP field. Clients that do not know OFAP ignore it. |

### `Operation<ApplicationTerms>`

A PAP operation. This mirrors the PAP operations extension (version 1). PAP is the authority for every field here except `terms` and `ofap_progress`. When the two differ, PAP wins.

An MCP tool returns it in `structuredContent.operation`. The agent confirms it at the PAP operations endpoint, never through an OFAP tool.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation_id` | string | Yes |  |
| `revision` | number | Yes | Starts at 1. Increases when the terms change. Only the latest revision can be confirmed. |
| `state` | [`OperationState`](https://openfinancialagent.org/spec/0.1/reference#OperationState) | Yes |  |
| `summary` | string | Yes | Plain-language terms that the customer can decide from alone. |
| `terms` | [`ApplicationTerms`](https://openfinancialagent.org/spec/0.1/reference#ApplicationTerms) | Yes |  |
| `expires_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) |  | Required while `proposed`. |
| `user_approval_required` | boolean | Yes | When true, a standing permission is not enough. The customer MUST approve this revision. |
| `url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  |  |
| `confirmation` | null or object with `revision`, `approved_by`, `confirmed_at` | Yes |  |
| `result` | null or object with `summary`, `data` | Yes |  |
| `ofap_progress` | [`OfapProgress`](https://openfinancialagent.org/spec/0.1/reference#OfapProgress) |  | OFAP reason why the operation is `in_progress`. Not a PAP field. Clients that do not know OFAP ignore it. |

### `OfapProgress`

| Field | Type | Required | Description |
|---|---|---|---|
| `reason` | one of `licensed_review`, `provider_decision`, `customer_action_on_distributor_page`, `payment_pending` | Yes |  |
| `url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) |  |  |
| `expected_by` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) |  |  |

### `ApplicationTerms`

Terms of an application or a conclusion. Every field the law needs as evidence is here, so that a confirmed revision records exactly what the customer accepted.

| Field | Type | Required | Description |
|---|---|---|---|
| `offer_id` | string | Yes |  |
| `product_id` | string | Yes |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes |  |
| `provider` | [`ProviderRef`](https://openfinancialagent.org/spec/0.1/reference#ProviderRef) | Yes |  |
| `pricing` | [`Pricing`](https://openfinancialagent.org/spec/0.1/reference#Pricing) | Yes |  |
| `options` | list of string | Yes |  |
| `start_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |
| `parties` | list of [`Party`](https://openfinancialagent.org/spec/0.1/reference#Party) | Yes |  |
| `declaration` | [`Declaration`](https://openfinancialagent.org/spec/0.1/reference#Declaration) | Yes |  |
| `needs_assessment` | [`NeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#NeedsAssessment) | Yes | It MUST be of the same sector as the offer. |
| `documents_delivered` | list of [`DeliveredDocument`](https://openfinancialagent.org/spec/0.1/reference#DeliveredDocument) | Yes | Documents delivered before this revision was proposed. Every document of the offer with `required_before_commitment: true` MUST be in this list. |
| `contact_consent` | [`ContactConsent`](https://openfinancialagent.org/spec/0.1/reference#ContactConsent) | Yes |  |
| `withdrawal_period_days` | number |  | Days the customer has to withdraw after conclusion, when the law gives the right. |

### `SubmitApplicationParams`

| Field | Type | Required | Description |
|---|---|---|---|
| `offer_id` | string | Yes |  |
| `parties` | list of [`Party`](https://openfinancialagent.org/spec/0.1/reference#Party) | Yes |  |
| `declaration` | [`Declaration`](https://openfinancialagent.org/spec/0.1/reference#Declaration) | Yes |  |
| `needs_assessment` | [`NeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#NeedsAssessment) | Yes |  |
| `documents_delivered` | list of [`DeliveredDocument`](https://openfinancialagent.org/spec/0.1/reference#DeliveredDocument) | Yes |  |
| `contact_consent` | [`ContactConsent`](https://openfinancialagent.org/spec/0.1/reference#ContactConsent) | Yes |  |
| `options` | list of string |  |  |
| `start_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |

### `SubmitApplicationResult`

When the operation succeeds, `result.data` holds an `ApplicationOutcome`.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation` | [`Operation<ApplicationTerms>`](https://openfinancialagent.org/spec/0.1/reference#Operation-ApplicationTerms) | Yes |  |

### `ApplicationOutcome`

| Field | Type | Required | Description |
|---|---|---|---|
| `application_id` | string | Yes |  |
| `evidence` | [`EvidenceRecord`](https://openfinancialagent.org/spec/0.1/reference#EvidenceRecord) | Yes |  |
| `handoff_url` | [`Url`](https://openfinancialagent.org/spec/0.1/reference#Url) | Yes | Page where the customer finishes the subscription. |

### `ConcludeParams`

Type: [`SubmitApplicationParams`](https://openfinancialagent.org/spec/0.1/reference#SubmitApplicationParams).

### `ConcludeResult`

Result of `conclude`. The operation MUST have `user_approval_required: true`. When it succeeds, `result.data` holds a `ConcludeOutcome`.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation` | [`Operation<ApplicationTerms>`](https://openfinancialagent.org/spec/0.1/reference#Operation-ApplicationTerms) | Yes |  |

### `ConcludeOutcome`

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |
| `evidence` | [`EvidenceRecord`](https://openfinancialagent.org/spec/0.1/reference#EvidenceRecord) | Yes |  |
| `documents` | list of [`FinancialDocument`](https://openfinancialagent.org/spec/0.1/reference#FinancialDocument) | Yes |  |

## 12. Evidence (core)

### `EvidenceRecord`

What the distributor keeps when an operation succeeds. It links the exact revision the customer accepted to the documents, the declaration, the needs assessment and the consent. The distributor MUST keep it for the period the law requires.

A PAP confirmation proves that an agent stated the customer approved a revision. It does not prove that the customer saw the terms. This record adds what was delivered and what was declared. It still does not prove that the customer read the documents.

| Field | Type | Required | Description |
|---|---|---|---|
| `record_id` | string | Yes |  |
| `operation_id` | string | Yes |  |
| `revision` | number | Yes |  |
| `approved_by` | one of `user`, `standing_permission` | Yes |  |
| `confirmed_at` | [`DateTimeString`](https://openfinancialagent.org/spec/0.1/reference#DateTimeString) | Yes |  |
| `documents_delivered` | list of [`DeliveredDocument`](https://openfinancialagent.org/spec/0.1/reference#DeliveredDocument) | Yes |  |
| `question_set` | [`QuestionSetRef`](https://openfinancialagent.org/spec/0.1/reference#QuestionSetRef) | Yes |  |
| `answers_sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes |  |
| `needs_assessment` | [`NeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#NeedsAssessment) | Yes |  |
| `contact_consent` | [`ContactConsent`](https://openfinancialagent.org/spec/0.1/reference#ContactConsent) | Yes |  |
| `terms_sha256` | [`Sha256`](https://openfinancialagent.org/spec/0.1/reference#Sha256) | Yes | SHA-256 of the canonical JSON (RFC 8785) of the confirmed `terms`. |

## 13. Agreements (core, level 4)

### `AgreementStatus`

Type: one of `pending`, `active`, `suspended`, `lapsed`, `cancelled`, `closed`, `expired`.

### `Agreement`

An agreement in force. In the insurance module, a policy.

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |
| `sector` | [`Sector`](https://openfinancialagent.org/spec/0.1/reference#Sector) | Yes |  |
| `status` | [`AgreementStatus`](https://openfinancialagent.org/spec/0.1/reference#AgreementStatus) | Yes |  |
| `product_id` | string | Yes |  |
| `line` | [`ProductLine`](https://openfinancialagent.org/spec/0.1/reference#ProductLine) | Yes |  |
| `provider` | [`ProviderRef`](https://openfinancialagent.org/spec/0.1/reference#ProviderRef) | Yes |  |
| `parties` | list of [`Party`](https://openfinancialagent.org/spec/0.1/reference#Party) | Yes |  |
| `pricing` | [`Pricing`](https://openfinancialagent.org/spec/0.1/reference#Pricing) | Yes | Price as agreed, in the form of the sector. |
| `options` | list of string | Yes |  |
| `start_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) | Yes |  |
| `renewal_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |
| `end_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  |  |
| `subjects` | list of [`Subject`](https://openfinancialagent.org/spec/0.1/reference#Subject) | Yes | What the agreement covers: a pet, a home, a vehicle, a property. |
| `documents` | list of [`FinancialDocument`](https://openfinancialagent.org/spec/0.1/reference#FinancialDocument) | Yes | Includes the proof of insurance when the product has one. |

### `Subject`

| Field | Type | Required | Description |
|---|---|---|---|
| `subject_ref` | string | Yes |  |
| `kind` | one of `pet`, `home`, `vehicle`, `property`, `valuable_item`, `business`, `person`, `other` | Yes |  |
| `display_name` | string | Yes |  |
| `attributes` | map of any | Yes | Attributes of the subject, following the line profile. |

### `GetAgreementParams`

Requires a signed-in PAP session with read scope.

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |

### `GetAgreementResult`

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement` | [`Agreement`](https://openfinancialagent.org/spec/0.1/reference#Agreement) | Yes |  |

### `AmendmentChange`

One change to an agreement. A sector module adds the changes of its sector.

**`kind: update_address`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `update_address` | Yes |  |
| `country` | [`CountryCode`](https://openfinancialagent.org/spec/0.1/reference#CountryCode) | Yes |  |
| `postal_code` | string | Yes |  |
| `city` | string | Yes |  |
| `street` | string | Yes |  |

**`kind: add_party`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `add_party` | Yes |  |
| `party` | [`Party`](https://openfinancialagent.org/spec/0.1/reference#Party) | Yes |  |

**`kind: remove_party`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `remove_party` | Yes |  |
| `party_ref` | string | Yes |  |

**`kind: add_option`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `add_option` | Yes |  |
| `option` | string | Yes |  |

**`kind: remove_option`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `remove_option` | Yes |  |
| `option` | string | Yes |  |

**`kind: change_excess`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `change_excess` | Yes |  |
| `excess` | [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) | Yes |  |

**`kind: add_subject`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `add_subject` | Yes |  |
| `subject` | [`Subject`](https://openfinancialagent.org/spec/0.1/reference#Subject) | Yes |  |

**`kind: remove_subject`**

| Field | Type | Required | Description |
|---|---|---|---|
| `kind` | `remove_subject` | Yes |  |
| `subject_ref` | string | Yes |  |

### `AmendAgreementParams`

Requires a signed-in PAP session with write scope.

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |
| `changes` | list of [`AmendmentChange`](https://openfinancialagent.org/spec/0.1/reference#AmendmentChange) | Yes |  |
| `effective_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) |  | When the changes take effect. Default: the earliest date the provider accepts. |
| `declaration` | [`Declaration`](https://openfinancialagent.org/spec/0.1/reference#Declaration) |  | Required when a change adds a party or a subject that the provider must price. |
| `needs_assessment` | [`NeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#NeedsAssessment) |  |  |
| `documents_delivered` | list of [`DeliveredDocument`](https://openfinancialagent.org/spec/0.1/reference#DeliveredDocument) |  |  |

### `AmendmentTerms`

| Field | Type | Required | Description |
|---|---|---|---|
| `agreement_number` | string | Yes |  |
| `changes` | list of [`AmendmentChange`](https://openfinancialagent.org/spec/0.1/reference#AmendmentChange) | Yes |  |
| `effective_date` | [`DateString`](https://openfinancialagent.org/spec/0.1/reference#DateString) | Yes |  |
| `pricing_before` | [`Pricing`](https://openfinancialagent.org/spec/0.1/reference#Pricing) | Yes |  |
| `pricing_after` | [`Pricing`](https://openfinancialagent.org/spec/0.1/reference#Pricing) | Yes |  |
| `amount_due_now` | [`Money`](https://openfinancialagent.org/spec/0.1/reference#Money) |  | Amount the customer pays at once, or gets back when negative, for example a premium adjustment. |
| `declaration` | [`Declaration`](https://openfinancialagent.org/spec/0.1/reference#Declaration) |  |  |
| `needs_assessment` | [`NeedsAssessment`](https://openfinancialagent.org/spec/0.1/reference#NeedsAssessment) |  |  |
| `documents_delivered` | list of [`DeliveredDocument`](https://openfinancialagent.org/spec/0.1/reference#DeliveredDocument) | Yes |  |

### `AmendAgreementResult`

Result of `amend_agreement`. The operation MUST have `user_approval_required: true` when `pricing_after` differs from `pricing_before`, when `amount_due_now` is set, or when a change adds or removes a party or a subject. A standing permission is not enough for these.

| Field | Type | Required | Description |
|---|---|---|---|
| `operation` | [`Operation<AmendmentTerms>`](https://openfinancialagent.org/spec/0.1/reference#Operation-AmendmentTerms) | Yes |  |

## 14. Errors (core)

### `OfapErrorCode`

Type: one of `not_licensed`, `not_available_in_region`, `product_not_found`, `offer_not_found`, `offer_expired`, `question_set_outdated`, `sensitive_field_received`, `documents_not_delivered`, `accuracy_not_confirmed`, `needs_assessment_invalid`, `agreement_not_found`, `amendment_not_allowed`, `not_supported`.

### `OfapError`

Error returned in an MCP tool result with `isError: true`, in `structuredContent.error`. PAP errors (`insufficient_scope`, `sign_in_required`, `terms_changed`) keep their PAP form.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | [`OfapErrorCode`](https://openfinancialagent.org/spec/0.1/reference#OfapErrorCode) | Yes |  |
| `message` | string | Yes |  |
| `fields` | list of [`FieldIssue`](https://openfinancialagent.org/spec/0.1/reference#FieldIssue) |  |  |

## 15. Conversation binding (PROPOSED, NOT DECIDED)

### `ConversationData`

PROPOSED. Some distributors expose only a company agent. If OFAP accepts them at level 2, each message that carries an OFAP object puts it here, in the message's structured data (`data` in a PAP conversation, `structuredContent` in a chat tool).

| Field | Type | Required | Description |
|---|---|---|---|
| `question` | object with `field`, `ask`, `schema` |  |  |
| `quote` | [`QuoteResult`](https://openfinancialagent.org/spec/0.1/reference#QuoteResult) |  |  |
| `documents` | list of [`FinancialDocument`](https://openfinancialagent.org/spec/0.1/reference#FinancialDocument) |  |  |
| `operation` | [`Operation<(ApplicationTerms\|AmendmentTerms)>`](https://openfinancialagent.org/spec/0.1/reference#Operation-ApplicationTerms-AmendmentTerms) |  |  |

## 16. Tool index

### `ToolName`

Type: one of `get_distributor`, `list_products`, `get_documents`, `get_question_set`, `quote`, `submit_application`, `conclude`, `get_agreement`, `amend_agreement`.

### `SessionRequirement`

Session that a tool needs (PAP draft 0.2).

Type: one of `none`, `signed_out_or_none`, `signed_in_read`, `signed_in_write`.

### `ToolSignatures`

Params and result type of each tool.

| Field | Type | Required | Description |
|---|---|---|---|
| `get_distributor` | object with `params`, `result` | Yes |  |
| `list_products` | object with `params`, `result` | Yes |  |
| `get_documents` | object with `params`, `result` | Yes |  |
| `get_question_set` | object with `params`, `result` | Yes |  |
| `quote` | object with `params`, `result` | Yes |  |
| `submit_application` | object with `params`, `result` | Yes |  |
| `conclude` | object with `params`, `result` | Yes |  |
| `get_agreement` | object with `params`, `result` | Yes |  |
| `amend_agreement` | object with `params`, `result` | Yes |  |

---

Licensed under the [Apache License 2.0](https://github.com/OFA-Foundation/open-financial-agent-protocol/blob/main/LICENSE).
