Developers

The regulated financial advice and guidance Infrastructure layer

Whistle runs the whole regulated journey: fact find, suitability, consent, outcome and the evidence behind it. Call it from your backend, or let an AI agent drive it over MCP. Both walk the same journey and produce the same record.

API MCP
THE JOURNEY Token step 1 Customer step 2 · context Questions step 3 · repeat Consent step 4 · evidence kept outputKind derived, never sent THEN, BY POLICY KIND A recommendation regulated-advice · I-ADV- personal, with a suitability letter A list of options guidance-only · I-GO- shape set by the policy WHERE IT RUNS YOUR SIDE Whistle widgetthe journey in your page Your backendholds the secret · calls REST Your AI agentcalls MCP tools AI gatewaychecks the token ·passes it through THE WHISTLE LAYER Whistle Platform APIrules engine · evidence · grants MCP serververifies the JWT · stores nothing THE RECORD One databasejourneys · evidence tenant-separated rows,all run in the UK (London) RUN BY WHISTLE FOR YOU Hosted agent (pilot)widget · MCP client Hosted journey webthe customer UI, your brand Bearer
REST · MCP

API documentation

Authentication, the journey primitives, the evidence chain and every endpoint ; with a worked integration for both a backend and an AI agent.

Read the guide →
Typed clients

The Whistle SDK

Generated from the same OpenAPI document the platform serves, so a contract change is a compile error rather than a production surprise.

Coming soon

Core concept

The regime decides the outcome. You don't.

A policy declares one regulatoryBasis. The kind of output a session can produce is derived from it and cannot be overridden by a caller ; which is what stops a journey that may only lay out options from quietly producing something that reads like advice.

Declared basis Output kind Reference What the customer receives
regulated-advicerecommendationI-ADV- A personal recommendation, with the suitability letter retrievable for the life of the record.
guidance-onlycomparisonI-GO- A list of options to weigh up: a table, a shortlist, whatever the policy calls for. No recommendation event is ever written.

The reference code says nothing about the tenant, the product or the provider. Those relationships live in the record, never in an identifier someone might parse.

How it is built

Design principles

Properties we hold to.

Policy is an artefact, not configuration

Every tenant's journey, copy, questions and rules compile to a content-hashed document in version control. Changing a disclosure is a reviewed change with an author and a date — and a rollback is a deployment, not a database edit.

specVersion · specHash

Full traceability and determinism. Every answer is evidence

Fact Find, consent, interactions , rule decisions and outcomes append to an immutable chain, each stamped with the exact policy artefact in force at the time. Reconstructing what a customer was shown two years ago is a query, not an archaeology project.

GET /events/:sessionId

Privacy Default-deny at the edge

Each section of a policy declares its audience. Anything marked internal is stripped before a response leaves the platform ; for a browser, a partner and an AI agent alike, by design not by habits.

audience: edge | internal

A commitment has a type

When a customer commits, the policy declares what that instruction is ; an annuity purchase, an investment, a pension transfer ; and it carries only the fields that kind can have.

service_instruction

Provenance on every write

Each fact and consent records how it was captured and by whom: the customer in a browser, an operator on the phone, or a partner's own system. A consent asserted on someone's behalf never looks like one they gave themselves.

capturedVia · actor

Service levels

What you can build against

99.9%Platform availability target, measured monthly on the journey and quoting surfaces.
<500ms95th percentile for journey calls, excluding time spent waiting on a third-party integration or provider.
AsyncExternal integration runs are queued and polled, so a slow background integration never becomes your slow request.
On this page

API & MCP

Integration guide

One core journey, then an overlay determined by the regime your policy declares. Call it from your backend over REST, or let an agent drive it over MCP ; the primitives, the guards and the record are identical.

Before you begin

You need a backend that can hold a secret and a policy provisioned for your tenant. Credentials arrive out of band: a clientId, a clientSecret and your tenantId. The secret never reaches a browser.

Every path below sits under the API prefix, e.g. https://<host>/api/v1. Every call except the token needs your bearer token and tenantId; journey calls also carry the clientId the platform issued for that customer.

If you would rather see a journey working before you write anything, skip to the technical pilot ; a complete agent host we run on your tenant, using the same gateway, token and tools your own integration will use.

The shape of a journey

Five calls get you from nothing to a completed fact find. What happens at step six depends on the regime ; and only on the regime.

THE JOURNEY Token step 1 Customer step 2 · context Questions step 3 · repeat Consent step 4 · evidence kept outputKind derived, never sent THEN, BY POLICY KIND A recommendation regulated-advice · I-ADV- personal, with a suitability letter A list of options guidance-only · I-GO- shape set by the policy WHERE IT RUNS YOUR SIDE Whistle widgetthe journey in your page Your backendholds the secret · calls REST Your AI agentcalls MCP tools AI gatewaychecks the token ·passes it through THE WHISTLE LAYER Whistle Platform APIrules engine · evidence · grants MCP serververifies the JWT · stores nothing THE RECORD One databasejourneys · evidence tenant-separated rows,all run in the UK (London) RUN BY WHISTLE FOR YOU Hosted agent (pilot)widget · MCP client Hosted journey webthe customer UI, your brand Bearer

1 · Authenticate

Client credentials over HTTP Basic, server to server. The token is short-lived and carries the tenants your integration was granted.

curl -X POST https://<host>/api/v1/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials" \
  -d "audience=whistle-api"

# → { "access_token": "…", "token_type": "Bearer", "expires_in": 1800 }

One token endpoint, /oauth/token, for both surfaces: the audience you ask for ; whistle-api or whistle-mcp ; is what decides where the token is good, and a token minted for one is refused by the other. Tokens are RS256 and last thirty minutes; the public keys are at GET /mcp-auth/.well-known/jwks.json if you want to verify them yourself.

2 · Customer context

Creates the customer's context and returns the clientId every later call uses. With your partner bearer token the journey is owned by your integration: the platform mints a fresh, tenant-bound clientId ; no cookie is set and no browser lead is touched.

POST /edge/client-context
{ "tenantId": "your-tenant" }

# → { "clientId": "…" }

Each partner call creates a new identity ; this boot does not resume. Call it once per customer journey, persist the clientId against your own customer record, and send it on every later call. Repeating the boot starts a different journey, not the same one.

Browser boots — the hosted journey and embedded widgets

Without a partner token the same endpoint boots an anonymous browser context: the platform resolves the lead behind the browser id (so refreshes and returning visits never fork into a new lead), returns the open sessionId to resume when one exists, and recognised says it has seen this person before ; without telling you who they are until they verify. Attribution ; channel, campaign, device ; is pinned to the lead at first touch and never overwritten, so pass it here if you have it.

POST /edge/client-context
{ "tenantId": "your-tenant", "browserId": "…", "channelCode": "partner-app" }

# → { "clientId": "…", "sessionId": "…", "leadId": "…", "recognised": false }

Identity

Verification for browser-booted customers is a magic link, driven by four calls. Until a customer verifies, they are a lead; after, they are a contact and the two generations are folded into one person.

  • POST/edge/identity/startSend the link
  • POST/edge/identity/verifyExchange the token
  • POST/edge/identity/restartRe-issue after expiry
  • POST/edge/identity/logoutDrop the verified identity, keep the browser

3 · Session

A session is one customer's run through one policy. It carries the policy version and hash in force when it started, which is what makes the record reconstructable later.

  • POST/sessionsStart
  • GET/sessionsList for this customer
  • GET/sessions/:sessionIdRead one, with its resume point
  • POST/sessions/:sessionId/advanceMove the journey on
  • POST/sessions/:sessionId/abandonEnd without an outcome

4 · Fact find

Ask what comes next, answer it, repeat. The question set, its order and its validation all come from the compiled policy ; you are not maintaining a copy of the questionnaire.

  • GET/agent/advice-scopeQuestion set and pinned spec version (?tenantId=<slug>)
  • GET/agent/questionsThe whole question set for this policy
  • GET/agent/next-question/:clientIdThe next one to ask
  • POST/agent/turnAnswer and receive the next, in one round trip
  • POST/agent/submit-answerAnswer without advancing
  • POST/agent/resubmit-answerCorrect an earlier answer
  • GET/agent/summary/:clientIdEverything answered so far
  • POST/agent/reset/:clientIdStart the fact find again

Preloading facts you already hold

If you know the customer's date of birth or pot value, seed it ; but a preloaded fact is not a confirmed one. It stays provisional until the customer confirms it, and the record says which it was.

  • POST/agent/preloadSeed facts with their provenance
  • POST/agent/confirm-preloadsCustomer confirms; provenance changes

Facts directly

Where a surface writes facts itself rather than walking questions, the same validation applies.

  • PUT/sessions/:sessionId/factsWrite facts
  • GET/sessions/:sessionId/factsRead them back

Two different things, deliberately separate. General consent is the policy's own declaration; special-category consent is the Article 9 gate, and health, lifestyle or similar facts are refused until it is on file.

  • POST/sessions/:sessionId/consentPolicy consent
  • POST/sessions/:sessionId/sensitive-data-consentArticle 9 gate

Writing a special-category fact before consent returns 403 SENSITIVE_CONSENT_REQUIRED. This is a platform guard, not a client-side rule ; it holds for a browser, your backend and an agent equally.

Which overlay applies

The basis is a property of the policy provisioned for your tenant, not a runtime negotiation: you are told it at onboarding, and it is not something a caller can set or switch. It is confirmed back to you at runtime on the payloads that carry an outcome ; the regulatoryBasis field on a comparison view and in a recommendation's engine block — and, on the MCP surface, by list_policies. The output kind is derived from it and never authored.

regulatoryBasis outputKind Reference Outcome route
regulated-advicerecommendationI-ADV- /agent/recommendation/:clientId + /letters/:sessionId
guidance-onlycomparisonI-GO- policy-specific ; annuity guidance today

Overlay · regulated advice

When the fact find is complete the pipeline runs and produces the output kind the basis implies. Read the state rather than assuming it is ready ; and branch on it rather than waiting for one value. IN_PROGRESS and DOCUMENT_PENDING are the states worth polling on; AWAITING_ADVISER, AWAITING_RELEASE, DOCUMENT_FAILED, CONSENTED, ABANDONED and NO_SESSION each end the poll and each need their own thing said to the customer.

  • GET/agent/recommendation-state/:clientIdPending, ready or unavailable
  • GET/agent/recommendation/:clientIdThe recommendation
  • GET/sessions/:sessionId/recommendationThe same, keyed by session
  • GET/letters/:sessionIdSuitability letters on file
  • GET/letters/:sessionId/:letterId/downloadThe document itself

Overlay · guidance only

A guidance-only session ends in a list of options for the customer to weigh up. Nothing in that list is singled out, ranked as best for them, or described as suitable ; the moment it were, the session would be giving advice under a basis that does not permit it.

What the list is made of is the policy's business, not the platform's. It might be a comparison table, a shortlist of products, a set of illustrated outcomes. So the routes that build and read it belong to the policy rather than to the core journey, and they differ between guidance policies.

The routes below are the annuity guidance policy ; the only guidance surface implemented today. A future guidance policy would keep the regime, the consents, the evidence chain and the instruction model exactly as they are, and present its options through routes of its own shape. Treat this section as a worked example, not as the guidance-only contract.

Annuity guidance · building the list

The platform runs a quote run against the provider bound in the policy and returns the options it came back with. A run is queued and polled: the POST answers with the run as created, and you read the outcome back.

POST /sessions/:sessionId/quote-runs   # → { quoteRunId, status: "REQUESTED" }
GET  /sessions/:sessionId/quote-runs/:quoteRunId
# poll while status is REQUESTED or SENT.
# COMPLETED → read the options; FAILED (no provider returned a quote, or a
# provider request failed) and EXPIRED are terminal → surface that to the
# customer instead of fetching the comparison.
GET  /sessions/:sessionId/comparison
  • POST/sessions/:sessionId/quote-runsAsk the provider panel for options
  • GET/sessions/:sessionId/quote-runsEvery run on this session
  • GET/sessions/:sessionId/quote-runs/:quoteRunIdOne run and what it returned
  • GET/sessions/:sessionId/comparisonThe list the customer is shown
  • POST/sessions/:sessionId/choose-quoteCustomer picks one
  • GET/annuity/providersProvider brands on the panel

An option the customer is not eligible for is returned and marked, with the reason ; never quietly dropped. Removing it would narrow the field on the customer's behalf, which is a recommendation made by omission.

Commitment

When a customer commits, the policy declares what the resulting service instruction is. It carries only the fields that kind can hold ; an annuity purchase has a quote and a provider; an investment has a wrapper and an amount and no quote at all.

  • GET/instructions/:instructionIdStatus and milestones
  • PUT/instructions/:instructionId/applicationApplication details, page by page

Reserved instruction types: annuity-guidance, investment, drawdown, retirement, savings, pension-transfer. A policy naming a type the platform has not implemented is refused at compile time rather than at runtime.

Agents over MCP

The same journey and the same guards, driven by a model instead of your backend. The platform still resolves the policy, owns the session and writes the record, so the agent holds no regulated state ; a conversation that drops loses nothing but the conversation. Use this instead of the REST setup above; nothing here depends on it.

1 · Credentials and the endpoint

The onboarding pack is the one the REST setup uses, plus the MCP endpoint. Your integration is granted the whistle-mcp audience. There is no self-registration, and the secret is shown once.

  • Tenant slug
    your-tenant
  • Client ID
    wpc_…
  • Client secret
    wpcs_… ; stays on your backend, never in the agent's prompt or context
  • Token URL
    https://<host>/api/v1/oauth/token ; your <host> arrives at onboarding
  • MCP endpoint
    https://<gateway-host>/<org>/mcp/<tenant>/server ; issued per tenant at onboarding

The endpoint is an AI gateway, not the platform API. It terminates the MCP transport, carries your bearer token through, and is where per-tenant rate limits and tool-call logging sit. An agent never calls the HTTP routes in the reference below ; the tool surface is the whole contract.

2 · A token for the gateway

Your backend exchanges the credentials exactly as in step 1 of the REST journey, with the MCP audience.

curl -X POST "$WHISTLE_API/oauth/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials" \
  -d "audience=whistle-mcp" \
  -d "scope=whistle:policy:read whistle:advice:write"

# → { "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 1800 }

Thirty minutes. Refresh on your side about a minute before expiry and hand the current token to the agent on each request, so the secret never reaches the model's context.

3 · Connect the agent

Any MCP client will do, over Streamable HTTP with the token as Authorization: Bearer. With the Claude API the connector does the protocol work for you.

{
  "model": "claude-sonnet-5",
  "mcp_servers": [{ "type": "url", "name": "whistle",
                    "url": "https://<gateway-host>/<org>/mcp/<tenant>/server",
                    "authorization_token": "<token from step 2>" }],
  "tools": [{ "type": "mcp_toolset", "mcp_server_name": "whistle" }],
  "messages": [{ "role": "user", "content": "Which propositions can you offer?" }]
}

# header: anthropic-beta: mcp-client-2025-11-20

Listing tools should return thirteen: the ten that walk the journey, plus get_section and submit_section for steps a policy marks as section-mode, and search_medical_reference. If it returns none, the token was minted for the wrong audience.

4 · The tools

They follow the order of the journey above. list_policies is the exception to every rule here: it takes no arguments at all, and what it returns is where the tenantSlug comes from ; there is no implicit tenant fallback. After that, the agent generates one clientId per customer and passes it with the slug on every call (search_medical_reference takes the slug but no clientId, since it reads a catalogue rather than a journey).

  • list_policies
    First call, no arguments. The propositions this token may run, each with its exact tenantSlug, its regulatoryBasis and the pinned spec version and hash. This is where the basis is confirmed at runtime.
  • get_advice_scope
    The question set and sections for a chosen tenant, with the spec version it is pinned to.
  • preload_session · confirm_preloads
    Seed facts you already hold, then have the customer confirm them.
  • get_next_question
    The pending question. Put it to the customer word for word.
  • submit_answer · resubmit_answer
    The answer unchanged, against the question's key. Repeat until done: true.
  • capture_consent
    Only once the customer has read it and agreed. Takes confirmed: true; the server fills in the provenance.
  • request_advice · get_recommendation
    Ask for the outcome, then read the state ; not a loop that waits for READY. See the states below.

READY is one of nine states, and several journeys never reach it. IN_PROGRESS and DOCUMENT_PENDING are the only two worth polling on. AWAITING_ADVISER (a referral), AWAITING_RELEASE, DOCUMENT_FAILED, CONSENTED, ABANDONED and NO_SESSION all end the poll: a client that waits for READY either spins forever or swallows a document failure. Treat a referral as a valid outcome to show the customer, not an error to retry.

Every tool result carries a relayContract. It tells the model to relay regulated text and figures as they are and never to interpret, advise or reassure. Keep it in the agent's instructions ; on this surface it is doing the job your UI code does on the other.

The tool names are deliberately generic; what comes back is not. get_recommendation returns whatever the policy's declared basis makes it ; a recommendation, or a list of options ; under the same label the REST surface uses, and on a guidance-only policy it never carries a personal recommendation at all.

Prove it before you build

Two setups, one question: how do you know the integration is right before you have written the thing that uses it? Start with a host you did not write, reduce the first proof to a couple of calls, then turn the dials one at a time.

Technical pilot: run our hosted agent first

Before wiring up your own LLM host, use ours. It is a small, complete host that Whistle runs for you: a chat widget on a demo page, a backend that holds the credentials and mints tokens, Claude as the model, and an MCP client to the Whistle server. Same gateway, same token, same tools your host will use.

  • See a whole journey on your tenant in a browser, end to end: the questions, a referral to a human where the policy calls for one, the outcome, consent.
  • Prove the plumbing before you write code. If the pilot works, your credentials, your gateway mapping and your tenant grants are right ; three things that are miserable to debug through a half-built host of your own.
  • Use it as the reference. A host has three jobs: mint and refresh the token, pass it to the MCP client on every request, and hold the model to the relay contract. The pilot does exactly those three and nothing else.
  • Get it
    We deploy it on your tenant and send you the link. Nothing to install.
  • Read it
    We share the source ; about 1,400 lines of TypeScript, no framework beyond Fastify. Run it locally with pnpm dev:agent-host and your step 1 credentials, then open http://localhost:4020/demo-partner.
  • Then
    Keep the same three jobs; swap in your model and your UI. Nothing on the Whistle side changes.

The smallest thing that proves it

One token and one call. Everything upstream of the journey ; credentials, audience, tenant grant, gateway route, policy resolution ; either works here or does not.

# REST
TOKEN=$(curl -s -X POST "$WHISTLE_API/oauth/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials" \
  -d "audience=whistle-api" | jq -r .access_token)

curl -s "$WHISTLE_API/agent/advice-scope?tenantId=$TENANT_SLUG" \
  -H "Authorization: Bearer $TOKEN"
# → { "tenantId", "specId", "version", "sections": [...], "questions": [...] }

# MCP ; same proof, one tool call, no arguments
# list_policies → your slug, basis and spec hash; an empty list means the grant is missing

Read what comes back rather than checking for a 200. On REST, specId and version tell you which compiled policy you actually resolved to ; the thing that makes a "works on my tenant" claim checkable. On MCP, list_policies adds regulatoryBasis and the spec hash, which is the one call that tells an agent which overlay it is about to run under.

Levers you can pull

Confidence is built by changing one thing at a time. These are the dials that exist on purpose.

  • Mock or live exchange
    Development tenants quote against a mock provider panel; the live exchange is a deployment setting, not a code change. Get the whole journey green before anyone's real rates are in play.
  • A different basis, same code
    Point the same integration at a guidance-only policy and then at an advised one. Your calls do not change; the outcome, its label and its reference prefix do. It is the quickest way to see that the regime decides the shape, not your client.
  • Quote without keeping the run
    Outside production, a quote request may carry persist: false to exercise the exchange without writing a run into the record. Production refuses it outright, so the escape hatch cannot follow you into live.
  • The record as your assertion
    After any run, GET /events/:sessionId and read what was actually recorded. > On a guidance-only policy RECOMMENDATION_GENERATED never appears ; assert its absence in your own suite and you have a regression guard that costs one call.
  • Funnel and guardrails
    /metrics/funnel and /metrics/rules show where sessions stopped and which rules fired, for the runs you just drove rather than for a reporting period.
  • Credentials as the kill switch
    Suspend, retire, revoke, close or hard-cut an integration without touching your deployment. Only closure, removing a tenant grant and a hard cut take effect on the next call ; see credential lifecycle below.

Going live

Everything above is the development environment. Production is a separate client ID, secret, base URL and MCP endpoint, and development credentials never work against it ; by construction, not by configuration. What does not change on the way over: the policy and its version hash, the tools, the contract, and the shape of the record.

The record

Every material act appends an immutable event stamped with the policy version and hash in force at the time ; the same chain whichever surface drove the journey.

GET /events/:sessionId

# FACT_RECORDED · SENSITIVE_DATA_CONSENT_CAPTURED · CONTACT_VERIFIED
# QUOTE_RUN_REQUESTED · QUOTE_RUN_COMPLETED · COMPARISON_GENERATED
# RECOMMENDATION_GENERATED · QUOTE_SELECTED · INSTRUCTION_ISSUED
# INSTRUCTION_MILESTONE_RECORDED · INSTRUCTION_STATUS_CHANGED · …

On a guidance-only policy RECOMMENDATION_GENERATED never appears. That absence is asserted end to end on every build ; a property of the platform rather than a convention.

  • GET/metrics/funnelJourney funnel for your tenant
  • GET/metrics/rulesWhich guardrails fired

Security

  • Token ; client credentials over HTTP Basic, server to server, short-lived.
  • Tenant ; resolved from the token, never read from the body. A client may only act for a tenant it was granted.
  • Audience ; policy sections marked internal are stripped by one serialiser before any response leaves, for browser, partner and agent alike.
  • Provenance ; every fact and consent records how it was captured and by whom, so a consent your system asserts is distinguishable from one the customer gave directly.
  • Special category ; refused until the Article 9 consent is on file.

Credential lifecycle

Credentials are managed for you and rotate without downtime: issue the new one, move over, retire the old. The important distinction is between stopping new tokens and refusing the ones already out there ; they are different controls, and only three of them are immediate.

Stops new tokens; issued ones drain

The integration is re-read on every authenticated call, but what that check enforces is the integration's own state and its tenant grants ; not which credential minted the token. So these bound the exposure to the remaining life of a token already issued, at most thirty minutes:

  • Retire a credential ; it mints no more tokens; a rotation's old secret ends here.
  • Revoke a credential ; same effect on tokens already issued. Revoking is about the secret, not about the sessions it opened.
  • Suspend the integration ; the token endpoint refuses it; work already in flight finishes. Resume puts it back.

Refused on the next call

  • Close the integration ; every authenticated call is denied from that moment, whatever token it carries.
  • Remove a tenant grant ; denied for that tenant immediately; grants are read live on every request rather than trusted from the token.
  • Hard cut ; the emergency stop. Immediate, cached so it does not depend on a database read, and reversible by us.

If you need an integration to stop now ; a leaked secret, an incident ; ask for a hard cut or a closure. Suspending or revoking is the right move for a planned change, but a token minted a minute earlier keeps working until it expires.

Endpoint reference

All paths under /api/v1. Everything except the token needs a bearer token and tenantId.

Authentication

  • POST/oauth/tokenAccess token · audience picks the surface
  • GET/mcp-auth/.well-known/jwks.jsonPublic keys

Context & identity

  • POST/edge/client-contextCreate or resume a customer
  • POST/edge/identity/startSend a magic link
  • POST/edge/identity/verifyVerify
  • POST/edge/identity/restartRe-issue
  • POST/edge/identity/logoutLog out

Sessions

  • POST/sessionsStart
  • GET/sessionsList
  • GET/sessions/:sessionIdRead
  • POST/sessions/:sessionId/advanceAdvance
  • POST/sessions/:sessionId/abandonAbandon
  • POST/sessions/:sessionId/consentPolicy consent
  • POST/sessions/:sessionId/sensitive-data-consentArticle 9 consent

Fact find

  • GET/agent/advice-scopeQuestion set and spec version
  • GET/agent/questionsQuestion set
  • GET/agent/next-question/:clientIdNext question
  • POST/agent/turnAnswer + next
  • POST/agent/submit-answerAnswer
  • POST/agent/resubmit-answerCorrect
  • POST/agent/preloadSeed facts
  • POST/agent/confirm-preloadsConfirm seeded facts
  • GET/agent/summary/:clientIdAnswers so far
  • POST/agent/reset/:clientIdRestart
  • PUT/sessions/:sessionId/factsWrite facts
  • GET/sessions/:sessionId/factsRead facts

Outcome ; advice & targeted support

  • GET/agent/recommendation-state/:clientIdReadiness
  • GET/agent/recommendation/:clientIdRecommendation
  • GET/sessions/:sessionId/recommendationSame, by session
  • GET/letters/:sessionIdSuitability letters
  • GET/letters/:sessionId/:letterId/downloadDownload

Outcome ; guidance only

  • POST/sessions/:sessionId/quote-runsRequest quotes
  • GET/sessions/:sessionId/quote-runsList runs
  • GET/sessions/:sessionId/quote-runs/:quoteRunIdOne run
  • GET/sessions/:sessionId/comparisonComparison
  • POST/sessions/:sessionId/choose-quoteCommit
  • GET/annuity/providersProvider panel

Commitment & record

  • GET/instructions/:instructionIdInstruction
  • PUT/instructions/:instructionId/applicationApplication details
  • GET/events/:sessionIdEvidence chain
  • GET/metrics/funnelFunnel
  • GET/metrics/rulesGuardrails fired