Send requests from your own systems, follow their progress and get webhooks when the status changes. This is the SUURA™ partner API, version 1.
Base address: https://www.suura.co.uk/api/v1. Everything is JSON over HTTPS.
sk_embed_test_.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"
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.
Origin header are refused with 403 browser_not_allowed, so a key pasted into a web page will not work.GET /me. It returns the partner name, the allowed values for kind and status, and the mode.| Field | Required | Notes |
|---|---|---|
customer.name | Yes | 2 to 80 characters. |
customer.email | Yes | A valid email address. |
kind | Yes | ncd (no claims discount letter), claims, policy, sar (full subject access request) or find (find previous insurers). |
consent.given | Yes | Must be true. Only send a request when the customer has given permission in your journey. |
consent.captured_at | Yes | When the customer agreed, ISO 8601, not in the future. |
consent.wording_version | Yes | Which version of your permission wording they saw. Keep a copy of every version. |
insurer | No | Up to 80 characters. |
partner_reference | No | Your 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.
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.
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.
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":"..."}.
| Status | Meaning |
|---|---|
received | SUURA has the request. |
searching | SUURA is searching for the records. |
records_ready | The records are ready for the insurer. |
cannot_find | No records could be found. |
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.
request.created, request.status_changed, webhook.test.POST /webhook/test. See recent attempts with GET /webhook/deliveries. Remove it with DELETE /webhook.2xx status within 4 seconds. In test mode we try once and do not retry. Retry rules for live mode are agreed with you.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
}
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.
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 look like {"error":{"code":"validation_failed","message":"...","details":[...]}}. Every reply carries SUURA-API-Mode: test.
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_body, invalid_idempotency_key | Not a JSON object, over 20 KB, or a badly formed key. |
| 401 | invalid_key | Missing, wrong, rotated or switched-off key. |
| 403 | browser_not_allowed | The call came from a browser. |
| 404 | not_found | No such endpoint, or a request that belongs to another partner. |
| 409 | idempotency_conflict, already_final, no_webhook | See the sections above. |
| 422 | validation_failed, invalid_url, invalid_status | The data was refused. details lists each problem. |
| 429 | rate_limited | More than 120 calls a minute per key. |
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.
Nothing below is open yet. This is the order we would work through with you.
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].