Developer docs

Send requests from your own systems, follow their progress and get webhooks when the status changes. This is the SUURA™ partner API, version 1.

Test mode only. Live access is not open yet. In test mode nothing reaches a real insurer, no ID check is run, and no email is sent to anyone. Use invented customer details only. Test records are deleted after 30 days. We are sharing this now so your technical team can see how it works and tell us what to change.

Quickstart

Base address: https://www.suura.co.uk/api/v1. Everything is JSON over HTTPS.

  1. Get a test key. Email [email protected] with your company name and the website addresses you will use. We send the key through a safe channel. It starts sk_embed_test_.
  2. Create a test request from your server.
  3. Move it along with the sandbox call, and watch the status change.
  4. Add a webhook so your system hears about each change.
curl -X POST https://www.suura.co.uk/api/v1/requests \
  -H "Authorization: Bearer $SUURA_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: case-1001-attempt-1" \
  -d '{
    "customer": { "name": "Sam Sample", "email": "[email protected]" },
    "kind": "ncd",
    "insurer": "Your company name",
    "partner_reference": "CASE-1001",
    "consent": {
      "given": true,
      "captured_at": "2026-11-08T10:15:00Z",
      "wording_version": "v1.2"
    }
  }'

The reply is 201 with the request, starting in status received:

{
  "ref": "EMB-3F9A1C07BD",
  "object": "request",
  "status": "received",
  "status_label": "Request received by SUURA.",
  "kind": "ncd",
  "partner_reference": "CASE-1001",
  "customer": { "name": "Sam Sample", "email": "[email protected]" },
  "consent": { "given": true, "captured_at": "2026-11-08T10:15:00.000Z", "wording_version": "v1.2" },
  "created": "2026-11-08T11:02:11.482Z",
  "updated": "2026-11-08T11:02:11.482Z",
  "events": [ { "at": "2026-11-08T11:02:11.482Z", "status": "received", "text": "Request received by SUURA." } ],
  "result": null,
  "test": true
}
# move it to "searching", then "records_ready"
curl -X POST https://www.suura.co.uk/api/v1/sandbox/requests/EMB-3F9A1C07BD/advance \
  -H "Authorization: Bearer $SUURA_TEST_KEY"

Authentication

Send your test secret key as a Bearer token: Authorization: Bearer sk_embed_test_.... Each key belongs to one partner and only sees that partner's requests.

Requests

Create a request POST /requests

FieldRequiredNotes
customer.nameYes2 to 80 characters.
customer.emailYesA valid email address.
kindYesncd (no claims discount letter), claims, policy, sar (full subject access request) or find (find previous insurers).
consent.givenYesMust be true. Only send a request when the customer has given permission in your journey.
consent.captured_atYesWhen the customer agreed, ISO 8601, not in the future.
consent.wording_versionYesWhich version of your permission wording they saw. Keep a copy of every version.
insurerNoUp to 80 characters.
partner_referenceNoYour own case number, up to 64 characters. Returned on every reply and webhook.

In live mode the customer's identity check and signed authority are done by SUURA before a request reaches the insurer. Test mode does neither.

Idempotency

Add an Idempotency-Key header (8 to 64 characters: letters, numbers, - or _) so a retry after a network error does not create a second request. The same key with the same body returns the original request with Idempotent-Replay: true. The same key with a different body returns 409 idempotency_conflict. Keys last 24 hours.

Retrieve GET /requests/{ref}

Returns the request, its list of events and, when the status is records_ready, a result. In test mode the result is an invented sample. The real format is agreed with each insurer.

List GET /requests

Newest first. Use limit (1 to 50, default 20) and pass next_cursor from the last reply as cursor for the next page. The reply is {"object":"list","data":[...],"has_more":true,"next_cursor":"..."}.

Statuses

StatusMeaning
receivedSUURA has the request.
searchingSUURA is searching for the records.
records_readyThe records are ready for the insurer.
cannot_findNo records could be found.

Webhooks

Register one https address and we send a POST each time something happens. Register or replace it with POST /webhook and {"url":"https://your-system.example.com/suura"}. The reply includes a signing_secret that starts whsec_. It is shown once. Posting again replaces the address and gives a new secret.

POST /suura HTTP/1.1
Content-Type: application/json
SUURA-Event-Id: evt_5d0c3a6f9b1e24a7
SUURA-Signature: t=1762599731,v1=9f2c...e1

{
  "id": "evt_5d0c3a6f9b1e24a7",
  "object": "event",
  "type": "request.status_changed",
  "created": "2026-11-08T11:02:11.482Z",
  "data": { "request": { "ref": "EMB-3F9A1C07BD", "status": "searching", "...": "..." } },
  "test": true
}

Check the signature

The signature is an HMAC-SHA256, in hex, of the timestamp, a dot, and the raw request body, using your signing secret. Verify it on the raw bytes before you parse the JSON, and reject events whose timestamp is more than 5 minutes old.

// Node.js
const crypto = require('crypto');
function verify(rawBody, header, secret) {
  const t = /t=(\d+)/.exec(header)[1], v1 = /v1=([a-f0-9]+)/.exec(header)[1];
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
  return v1.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
# Python
import hmac, hashlib, re, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    t = re.search(r"t=(\d+)", header).group(1)
    v1 = re.search(r"v1=([a-f0-9]+)", header).group(1)
    if abs(time.time() - int(t)) > 300:
        return False
    expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(v1, expected)

Events can arrive more than once or out of order. Use the event id to ignore repeats and the request's updated time to keep the newest.

Sandbox

POST /sandbox/requests/{ref}/advance moves a test request on without waiting for us: received to searching to records_ready. Send {"status":"cannot_find"} (or any status) to jump to it. A webhook fires each time. A request that is already finished returns 409 already_final. This endpoint does not exist in live mode.

Errors and limits

Errors look like {"error":{"code":"validation_failed","message":"...","details":[...]}}. Every reply carries SUURA-API-Mode: test.

HTTPCodeWhen
400invalid_body, invalid_idempotency_keyNot a JSON object, over 20 KB, or a badly formed key.
401invalid_keyMissing, wrong, rotated or switched-off key.
403browser_not_allowedThe call came from a browser.
404not_foundNo such endpoint, or a request that belongs to another partner.
409idempotency_conflict, already_final, no_webhookSee the sections above.
422validation_failed, invalid_url, invalid_statusThe data was refused. details lists each problem.
429rate_limitedMore than 120 calls a minute per key.

Embed

The embed button lets a customer give permission inside your own website or app, with SUURA's pages shown in a frame on web addresses you approve. Requests made that way appear in the same list and fire the same webhooks. Use the button when the customer is in your journey, and the API when your system already holds the customer's permission. The embed is also test mode only. Try it on the demo page.

Going live

Nothing below is open yet. This is the order we would work through with you.

  1. Pilot terms and a data processing agreement signed, setting out who is responsible for what.
  2. Security review on both sides. We plan an independent penetration test and Cyber Essentials, and expect to share the results with you.
  3. Permission wording. Your compliance team approves the wording your customers see, and we agree what you must keep as evidence.
  4. Result format. We agree what a returned record looks like, and how it reaches the customer.
  5. Live keys. Issued separately from test keys, to approved servers and web addresses only.

Not built yet: live keys, retries and signed responses, per-insurer connections into policy systems, and a mobile kit (SDK). We would agree each with you before building it. Questions: [email protected].