Draft 0.1
Open Financial Agent Protocol, draft 0.1
Pages and sections
Draft 0.1 covers insurance. A later draft can break this one until 1.0. The source of truth is schema.ts, and the schema reference is generated from it. When this page and schema.ts differ, schema.ts wins.
1.Overview
OFAP defines how an AI agent finds, prices, subscribes to and services a regulated financial product with any distributor. It also defines the evidence that each step leaves, so that a sale through an agent meets the same rules as a sale on the distributor's own site.
OFAP is an extension on top of two protocols:
- MCP carries every call. Each OFAP primitive is an MCP tool. A tool puts its result in
structuredContent, with a short plain-text summary incontent. - PAP carries the customer's session and every action with a business effect, through its operations extension. OFAP defines only the financial
termsof those operations.
The protocol has a core that does not depend on the sector, and one module per sector. A module adds the pricing, the needs assessment and the documents that the law of its sector asks for. Draft 0.1 defines the insurance module. Sector modules gives the rules for a later one, for example credit or investment.
1.1Roles
- Agent. The AI agent that acts for the customer. It calls the tools.
- Distributor. The company that runs the MCP server and sells the product: an insurer, a broker, a tied agent, an MGA or a comparator.
- Provider. The party that carries the risk. In insurance, the insurer. It can be the distributor itself.
- Customer. The person or organisation that buys. Every commitment needs their approval through PAP.
2.Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be read as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.
- An amount is a
Moneyobject. Itsamountis a decimal string such as"14.90", so that no value is lost to floating point. - Dates and times are ISO 8601. Countries are ISO 3166-1 alpha-2 codes, currencies ISO 4217 codes, and languages BCP 47 tags.
- A hash is a lower-case hex SHA-256. The hash of a JSON value is the SHA-256 of its canonical JSON (RFC 8785).
3.Discovery
A server declares OFAP 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. The value is an OfapSettings object: the protocol version, the conformance level, the sector modules, the product lines and the countries the server covers, and whether it can conclude an agreement inside the agent.
4.Conformance levels
| Level | Adds the tools | Session (PAP) |
|---|---|---|
| 1. Inform | get_distributor, list_products, get_documents |
None |
| 2. Quote | get_question_set, quote |
None; None, or signed out |
| 3. Subscribe | submit_application, conclude |
None, or signed out |
| 4. Service | get_agreement, amend_agreement |
Signed in, read scope; Signed in, write scope |
A server that declares a level MUST implement every tool of that level and of the levels below it. Level 3 needs only one of submit_application and conclude. A call to a tool above the declared level returns the error not_supported.
5.Tools
| Tool | Level | Session (PAP) | Effect | Params → result |
|---|---|---|---|---|
get_distributor |
1 | None | Read-only | GetDistributorParams → GetDistributorResult |
list_products |
1 | None | Read-only | ListProductsParams → ListProductsResult |
get_documents |
1 | None | Read-only | GetDocumentsParams → GetDocumentsResult |
get_question_set |
2 | None | Read-only | GetQuestionSetParams → GetQuestionSetResult |
quote |
2 | None, or signed out | Read-only | QuoteParams → QuoteResult |
submit_application |
3 | None, or signed out | Returns a PAP operation | SubmitApplicationParams → SubmitApplicationResult |
conclude |
3 | None, or signed out | Returns a PAP operation | ConcludeParams → ConcludeResult |
get_agreement |
4 | Signed in, read scope | Read-only | GetAgreementParams → GetAgreementResult |
amend_agreement |
4 | Signed in, write scope | Returns a PAP operation | AmendAgreementParams → AmendAgreementResult |
The session column follows PAP draft 0.2. A read-only tool sets the MCP readOnlyHint annotation. The tools that return an operation commit nothing on their own: see Applications.
6.Distributor
get_distributor returns who sells, and under which licences. Its domain MUST match the server's own domain. Each Licence names the country, the regulator, the public register and the identifier, the role, and the sectors it covers. A licence can apply to subdivisions only, for example some US states. verification_url is the public page where anyone can check the entry, and complaints_url is where a customer can complain.
7.Products and documents
list_products returns Product objects, filtered by country, sector or line. Each product lists its subscription paths:
conclude: the agent concludes the agreement withconclude.application: the agent sendssubmit_application, and the customer finishes on the distributor's page.handoff: the customer goes to the distributor's page with an offer reference.
get_documents returns the FinancialDocument objects of a product, an offer or an agreement. Each document has a version, set by the distributor, and the SHA-256 of the exact file at its url. A new text MUST get a new version. required_before_commitment is true when the law requires that the customer receives the document before they commit, for example the IPID.
8.Question sets
get_question_set returns the questions of a product as a JSON Schema (2020-12) object, answers_schema. Each property is one question. OFAP adds three keywords to a property:
| Keyword | Meaning |
|---|---|
x-collected-by |
agent, the default, or distributor_page |
x-profile-key |
The key of the matching question in the line profile |
x-ask |
A short question the agent can ask the customer, in plain words |
A value marked distributor_page 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. A server that gets such a value anyway stores nothing and returns sensitive_field_received.
A change in any question MUST change the version of the question set. Answers to an old version get question_set_outdated.
8.1Line profiles
A line profile is the common core of questions for one product line. A question set MUST include every required core question of its line, with the same key, and MAY add its own questions. Two products of the same line can then be quoted from the same answers. A profile needs a second distributor of the same line before it is stable.
insurance/pet@0.1
Derived from: pet insurer, FR, DE, SE.
| Key | Type | Required | Question or note |
|---|---|---|---|
country |
string | Yes | In which country do you live? |
postal_code |
string | Yes | What is your postal code? |
municipality_code |
string | Official municipality code, where the tariff uses it (for example in Sweden). | |
species |
dog, cat |
Yes | Is it a dog or a cat? |
pet_name |
string | Yes | What is your pet's name? |
breed |
string | Yes | What breed is it? |
is_mixed_breed |
boolean | ||
mixed_breed_size |
small, medium, large, giant |
Dogs of mixed breed with unknown parents only. | |
sex |
female, male |
Yes | |
is_neutered |
boolean | Yes | Is your pet neutered or spayed? |
date_of_birth |
string (date) | Yes | When was your pet born? An approximate date is fine. |
lifestyle |
indoor, outdoor |
Cats only. Do not set for dogs. |
insurance/home@0.1
Derived from: home insurer, ES.
| Key | Type | Required | Question or note |
|---|---|---|---|
country |
string | Yes | In which country do you live? |
street |
string | Yes | What is the street and number of the property? |
city |
string | Yes | |
postal_code |
string | Yes | What is the postal code? |
usage |
tenant, owner_occupied, landlord, second_home |
Yes | Do you rent it, live in it as the owner, rent it out, or use it as a second home? |
floor_area_m2 |
number | Yes | How many square metres is the property? |
property_type |
house, apartment |
Not asked by the source server today. Most home tariffs need it. |
9.Quote
quote takes a product, a question set reference and the answers. The agent MAY send partial answers. The result has one of three states:
needs_input:missing_fieldslists the questions that are missing, not valid or not accepted. The agent asks the customer and callsquoteagain with more answers.in_progress: the agent reads the quote again afterretry_after_seconds, and sends only thequote_request_id.complete:offerslists the priced offers, andanswers_sha256is the hash of the answers used. Whenoffersis empty,no_offeris REQUIRED. It gives a stablereason_code, such asage_not_accepted, and a message the agent can pass to the customer.
This one loop replaces the multi-step flow tools (action, stateUpdates, sessionId) that most servers use.
An Offer holds the provider, the pricing in the form of its sector, the last moment it can be accepted (valid_until), and its subscription paths. When indicative is true, the price can change after checks. An offer with the handoff path MUST have a handoff_url, and that URL MUST NOT contain personal data.
10.Applications
submit_application and conclude return a PAP operation in structuredContent.operation. The agent confirms it at the PAP operations endpoint, never through an OFAP tool. PAP is the authority for every field of the operation except terms and ofap_progress. When the two differ, PAP wins.
The terms of an application are an ApplicationTerms object. They hold every field the law needs as evidence, so that a confirmed revision records exactly what the customer accepted:
- the offer, the provider, the pricing and the options;
- the parties, each with its roles, for example subscriber, insured, beneficiary or payer;
- the declaration: the answers, the question set version, their
answers_sha256, andaccuracy_confirmed; - the needs assessment, which MUST be of the same sector as the offer;
- the documents delivered before this revision, each with its version and hash;
- the contact consent, and the withdrawal period when the law gives one.
Every document of the offer with required_before_commitment: true MUST be in documents_delivered. Otherwise the server returns documents_not_delivered.
accuracy_confirmed is true only when the customer confirmed that the answers are true and complete. The agent MUST NOT set it to true for invented or test data.
A conclude operation MUST have user_approval_required: true, so a standing permission is not enough. Only the latest revision of an operation can be confirmed, and the revision increases when the terms change. While an operation is in_progress, ofap_progress can say why: a licensed person reviews the application, the provider decides, the customer must act on the distributor's page, or a payment is pending.
When the operation succeeds, result.data holds an ApplicationOutcome for an application, with the page where the customer finishes, or a ConcludeOutcome for a conclusion, with the agreement number and its documents. Both carry the evidence record.
11.Evidence
When an operation succeeds, the distributor keeps an EvidenceRecord. It links the exact revision the customer accepted to the documents delivered, the declaration, the needs assessment and the consent. Its terms_sha256 is the hash of the confirmed terms. The distributor MUST keep the record 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. The evidence record adds what was delivered and what was declared. It still does not prove that the customer read the documents.
12.Agreements
get_agreement returns an Agreement. It needs a signed-in PAP session with read scope. amend_agreement needs write scope. It takes a list of AmendmentChange values and returns a PAP operation whose terms are an AmendmentTerms object: the changes, the effective date, the pricing before and after, and the amount the customer pays at once, if any.
The operation MUST have user_approval_required: true when the price changes, 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. A change that adds a party or a subject that the provider must price needs a declaration.
13.Errors
A tool returns an error in an MCP tool result with isError: true, as an OfapError in structuredContent.error. PAP errors such as insufficient_scope, sign_in_required and terms_changed keep their PAP form.
| Code | Meaning |
|---|---|
not_licensed |
The distributor holds no licence for this country, subdivision, sector or line. |
not_available_in_region |
The product is not sold where the customer is. |
product_not_found |
No product has this product_id. |
offer_not_found |
No offer has this offer_id. |
offer_expired |
The offer is past its valid_until. |
question_set_outdated |
The answers follow an old version of the question set. |
sensitive_field_received |
A value marked distributor_page was sent through the agent. Nothing was stored. |
documents_not_delivered |
A document with required_before_commitment is not in documents_delivered. |
accuracy_not_confirmed |
The declaration does not have accuracy_confirmed: true. |
needs_assessment_invalid |
The needs assessment is missing, of another sector, or its outcome does not allow the application. |
agreement_not_found |
No agreement has this agreement_number. |
amendment_not_allowed |
The provider does not accept this change to the agreement. |
not_supported |
The tool is above the conformance level the server declares. |
14.Sector modules
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 inKnownProductLine; - one member of
ProductDetails,PricingandNeedsAssessment, each with asectorfield that holds the module's value; - its values in
ProviderRole,DistributorRole,RegisterIdentifierType,DocumentType,PartyRoleandAmendmentChange, when the sector needs them; - which of its documents are
required_before_commitment; - its line profiles, 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 before commitment, creditworthiness under the CCD and the MCD, APR and total cost of credit.
- Investment. KID under PRIIPs, suitability or appropriateness under MiFID II, fees and risk indicator.
15.Insurance module
- Pricing. An
InsurancePricingobject: thePremium(amount, period, and whether taxes are in it), the same price for other periods, the cover summary, the options and the start date. TheCoverSummarylists what is included, the limits and reimbursement rates, the excess, the main exclusions and the waiting period. - Needs assessment. An
InsuranceNeedsAssessmentrecords the customer's demands and needs before the contract is concluded (IDD Art. 20(1)). When the distributor gave a personal recommendation,advice_rationaleis REQUIRED and says why the offer meets the need. - Documents. The IPID, the general and special conditions, the statement of demands and needs, and the proof of insurance.
- Parties. The insured and the beneficiary, next to the subscriber and the payer of the core.
- Amendments. A change of excess, and the addition or removal of a subject, such as a pet, a home or a vehicle.
- Registers. ORIAS in France, the FCA register in the United Kingdom, NIPR in the United States and the DGSFP register in Spain.
16.Security and privacy
- Values marked
distributor_pagenever pass through the agent. - Data in a
PartyMUST be limited to what the product needs. Identity numbers MUST NOT be in a party. - A
handoff_urlMUST NOT contain personal data. - Every commitment goes through a PAP operation, and the evidence record keeps the hash of the terms the customer approved.
Report a weakness that could harm a live implementation privately, as SECURITY.md describes.
17.Checks
The repository builds a worked pet insurance flow and checks it against schema.json and against the rules that a JSON Schema cannot express:
- R1 profiles compile.
- R2 a question set has every required question of its profile.
- R3 answers match their question set.
- R4 no
distributor_pagefield in agent answers. - R5
answers_sha256is the SHA-256 of the canonical JSON (RFC 8785). - R6 every document required before commitment was delivered, same version and digest.
- R7 accuracy confirmed before a proposal.
- R8 needs assessment and pricing match the offer's sector.
- R9 an application needs the customer's own approval.
- R10 a non-life insurance product has an IPID required before commitment.
- R11 the evidence hash matches the confirmed terms.
- R12 a price change, an amount due, or a change of party or subject needs the customer's own approval.
- R13 a handoff offer has a
handoff_url. - R14 an empty quote gives a reason.
18.Open points
- Conversation-only distributors.
ConversationDatais a proposal. Decide whether a distributor that exposes only a company agent can reach level 2 if it returns structured quotes. - PAP fields.
Operationmirrors PAP draft 0.2 and rejects unknown fields. It must accept new PAP fields before 1.0. - Credit and investment modules. Draft each one on the rules for sector modules (section 8 of
schema.ts), with at least two distributors as input. - Profiles. A second distributor for the pet and home profiles, and profiles for commercial lines.
- Legal review of the IDD references, country by country.
- Governance. FINOS (Linux Foundation) is a candidate home, next to MCP and A2A.
To give feedback, open an issue.