TPP · Insurance · Quotation · TPP-Led

TPP-Led Flow 8 min read

You collect quote inputs, KYC, and surface the LFI’s hosted payment URL to the customer inside your own app. Document delivery also lives with you. The LFI handles underwriting, payment hosting, and policy issuance; your TPP is the customer-facing surface for everything else.

01 End-to-end sequence

TPP-Led flow

Sequence diagramInsurance Quotation — TPP-Led FlowClick to expand
02 Steps 1–2 — Token & Quote

Same as LFI-Led for quote creation

Obtain a Client Credentials token (insurance scope) and POST the quote request exactly as in LFI-Led. The mode is not declared on Create Quote — it’s determined by the LFI’s response to PATCH Accept.

03 Step 3 — PATCH Accept Quote

Accept and discover PolicyIssuanceAllowed

Customer picks a quote; PATCH it with the accept data and your Subscription.Webhook. The LFI’s response signals whether the flow is TPP-Led: a 200 response with data.PolicyIssuanceAllowed means you are responsible for the steps listed as true.

200 response (TPP-Led)json
{
  "data": {
    "PolicyIssuanceAllowed": {
      "CustomerVerification": true,
      "Payment": true,
      "PolicyDocuments": true
    }
  }
}

All three flags true is the full TPP-Led mode. You MUST honour the declaration — do not perform a step set to false even if you can technically do so.

04 Step 4 — PATCH Submit KYC

Collect KYC and submit to the LFI

After ApplicationPending arrives, collect the customer’s KYC in your app (see User Journeys — KYC capture for the screens to present). Submit by PATCHing the same quote endpoint a second time with the gathered data.

PATCH /motor-insurance-quotes/{QuoteId} (Submit KYC, decoded)json
{
  "Data": {
    "PolicyStartDate": "2026-06-01",
    "PolicyHolder": {
      "EmiratesId": "784-1990-XXXXXXX-X",
      "EmiratesIdExpiryDate": "2030-01-15",
      "FullName": { "en": "Aisha Al Marri" },
      "DateOfBirth": "1990-05-12",
      "Address": {
        "AddressLine": ["Villa 12, Street 5"],
        "PostCode": "12345",
        "TownName": "Dubai",
        "CountrySubDivision": "Dubai",
        "Country": "AE"
      }
    },
    "AdditionalDeclarations": {
      "NoClaimsLastFiveYears": true
    }
  }
}

The body conforms to the sector’s accept-quote schema. Required fields vary by sector — consult the OpenAPI spec. If KYC fails, you receive 400 with a descriptive message; surface it to the customer and allow retry.

05 Step 5 — ApplicationApproved event

Redirect the customer to the LFI-hosted payment URL

The LFI processes KYC asynchronously and, on success, emits ApplicationApproved with a BrokerInstructions[].Url pointing at its hosted payment page.

ApplicationApproved eventjson
{
  "QuoteStatus": "ApplicationApproved",
  "BrokerInstructions": [
    {
      "ActionRequired": "Customer must complete premium payment at the LFI-hosted payment page.",
      "Url": "https://pay.examplelfi.ae/checkout/sess-c93e1f4a"
    }
  ]
}

Redirect the customer to BrokerInstructions[0].Url. They pay on the LFI’s page; the LFI redirects them back to a return URL you nominated when configuring your webhook or out-of-band with the LFI.

Single-use URL

Do not cache, log, or replay the URL. If the customer abandons and returns, request a fresh URL by asking the LFI to re-emit (typically via your support process or by re-triggering the flow). The LFI will emit a new PaymentRequired event.

06 Step 6 — POST Create Policy

Issue the policy

After payment confirmation (which you can correlate with the customer’s return from the LFI’s payment page or by waiting for the LFI’s next status event), call POST /{type}-insurance-policies with the QuoteId. The body is similar to the KYC submission but represents the formal policy creation request.

The LFI runs its issuance and responds 201. The InsurancePolicyId and policy documents arrive in the subsequent PolicyIssued event — not in this response body.

07 Step 7 — PolicyIssued event

Verify and surface documents to the customer

In TPP-Led mode the PolicyIssued event carries every customer-facing document the LFI would normally deliver itself. Verify each Hash against the decoded Content before presenting to the customer.

PolicyIssued event with Documentsjson
{
  "QuoteStatus": "PolicyIssued",
  "Documents": [
    {
      "Type": "Policy Booklet",
      "FileName": "policy-booklet.pdf",
      "ContentType": "application/pdf",
      "Content": "JVBERi0xLjQKJeLjz9MKMyAwI...",
      "HashType": "SHA256",
      "Hash": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
    },
    {
      "Type": "Terms & Conditions",
      "FileName": "terms.pdf",
      "ContentType": "application/pdf",
      "Content": "JVBERi0xLjQKJeLjz9MKMyAwI...",
      "HashType": "SHA256",
      "Hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    }
  ]
}

Hash verification

Node — SHA-256 verificationts
import { createHash } from 'node:crypto'

async function verifyDocument(doc: {
  Content: string
  Hash: string
  HashType: string
}): Promise<boolean> {
  if (doc.HashType !== 'SHA256') {
    throw new Error('Unsupported HashType: ' + doc.HashType)
  }
  const bytes = Buffer.from(doc.Content, 'base64')
  const computed = createHash('sha256').update(bytes).digest('hex')
  return computed === doc.Hash
}
Python — SHA-256 verificationpython
import base64, hashlib

def verify_document(doc: dict) -> bool:
    if doc["HashType"] != "SHA256":
        raise ValueError(f"Unsupported HashType: {doc['HashType']}")
    raw = base64.b64decode(doc["Content"])
    return hashlib.sha256(raw).hexdigest() == doc["Hash"]
Do not deliver mismatches

If a document’s computed hash does not match the supplied Hash, treat the document as corrupt or tampered. Do not surface it to the customer. Log the x-fapi-interaction-id from the event delivery, raise a support ticket including that ID and the QuoteId, and request the LFI re-emit the event.

08 Step 8 — Completed event

Surface final policy state and reconcile commission

On Completed, finalise the policy in your records, surface it as live to the customer, and reconcile any commission against the event’s Commission block. PaymentMethod: ThroughAPIHub means the Hub will route payment to you; the commission and timing are governed by the AlTareq Commercial and Pricing Model.

No further events follow. You can let the subscription lapse, or explicitly PATCH the quote with IsActive: false if you prefer to clean up — though no further events would be sent regardless.