Docs
Sign up free
API reference

Asynchronous checks

POST https://checksharecode.co.uk/api/check/async queues a check and answers at once with its id. We run it, retry it if gov.uk fails, then post the result to your callback URL, or keep it for you to fetch with GET /api/check/async/:id. Same request body, same result and same error codes as POST /api/check.

Use it when you would rather not hold a request open while gov.uk answers, when you check many applicants at once, or when you want us to retry a check gov.uk failed. It works on every plan, and with sandbox keys too.

Headers

Authorizationstringrequired
Bearer followed by your key, as for POST /api/check.
Example: Bearer rtw_live_…
Idempotency-Keystring
Any string up to 255 characters, unique per check you mean to run. Sending the same key again returns the first check with 200, so a retry after a network error never runs or charges a second check.
Example: applicant-4411-check-1

Body

share_code, date_of_birth, company_namestringrequired
The same three fields, with the same rules, as POST /api/check.
callback_urlstring
Where we post the result when the check ends. Without it we use your default URL for the mode, set on Webhooks, and without that you poll. See Webhooks.
Example: https://api.acme.example/hooks/rtw?applicant=4411
client_referencestring
Your own id for the check, up to 200 characters, given back in every response and webhook. Use an opaque id, never the applicant’s name, share code or date of birth.
Example: applicant-4411
Request
curl https://checksharecode.co.uk/api/check/async \
  -H "Authorization: Bearer $CHECKSHARECODE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: applicant-4411-check-1" \
  -d '{
    "share_code": "AB1CD2EF3",
    "date_of_birth": "1990-01-01",
    "company_name": "Acme Ltd",
    "callback_url": "https://api.acme.example/hooks/rtw?applicant=4411",
    "client_reference": "applicant-4411"
  }'
202 Accepted
{
  "id": "chk_5f2a8c1e9b7d4a6f8e3c2b1a0d9f8e7c",
  "status": "queued",
  "mode": "live",
  "client_reference": "applicant-4411",
  "callback_url": "https://api.acme.example/hooks/rtw?applicant=4411",
  "attempts": 0,
  "result": null,
  "error": null,
  "result_expires_at": null,
  "result_expired": false,
  "created_at": "2026-09-28T10:00:00.000Z",
  "completed_at": null
}

The 202 has a Location header with the check’s URL. Bad input, a bad key, an exhausted quota and rate limits come back straight away with the usual error, so you never wait for a webhook to learn a request was wrong.

Get a check

GET /api/check/async/:id with the same key returns the check as it stands. If you set a callback URL you don’t need to poll: the webhook carries this same object.

GET /api/check/async/:id, 200 OK
{
  "id": "chk_5f2a8c1e9b7d4a6f8e3c2b1a0d9f8e7c",
  "status": "succeeded",
  "mode": "live",
  "client_reference": "applicant-4411",
  "callback_url": "https://api.acme.example/hooks/rtw?applicant=4411",
  "attempts": 1,
  "result": { …the same object POST /api/check returns… },
  "error": null,
  "result_expires_at": "2026-10-05T10:00:14.000Z",
  "result_expired": false,
  "created_at": "2026-09-28T10:00:00.000Z",
  "completed_at": "2026-09-28T10:00:14.000Z"
}
FieldTypeMeaning
idstringThe check’s id, chk_ and 32 characters.
status"queued" | "running" | "succeeded" | "failed"Where the check is. succeeded and failed are final.
mode"live" | "test"Whether a live or a sandbox key queued it.
resultobject | nullWhen succeeded: exactly what POST /api/check returns, photo and PDF included.
error{ code, message } | nullWhen failed: the same code and message POST /api/check would have returned.
attemptsintegerHow many times we ran it. More than 1 means we retried.
callback_urlstring | nullWhere the result is posted. null means poll only.
client_referencestring | nullYour id for the check, as you sent it.
result_expires_atdate-time | nullWhen we delete the result: 7 days after the check ends.
result_expiredbooleantrue once the result is deleted. status and error stay readable.
created_at, completed_atdate-timeWhen it was queued, and when it ended.

Outcomes and retries

  • Same outcomes as the sync API. What POST /api/check answers with 200 ends as succeeded. What it answers with an error ends as failed with that error code, so NOT_FOUND and DOB_MISMATCH are failed checks. Each is charged the way POST /api/check charges it.
  • We retry for you. TIMEOUT, BUSY and INTERNAL are run again, up to 3 attempts in all, after about 30 seconds and then 2 minutes. GOVUK_UNEXPECTED is run once more. Only the final outcome is charged.
  • Retries add time. A check that needs every retry takes a few minutes to end.

How long we keep the result

The result, photo and PDF included, is kept encrypted for 7 days after the check ends, so we can deliver it to you. Then it is deleted, with your client_reference and callback_url: result becomes null and result_expired becomes true. Save what you need when it arrives. The check’s status and error code stay readable for 30 days.

Limits and errors

  • Checks in progress count. A queued or running live check counts against your monthly allowance, so a free account can’t queue more checks than it has left (402 QUOTA_EXCEEDED).
  • 100 in progress at once. Past that, POST answers 429 RATE_LIMITED with Retry-After.
  • Unknown ids. GET answers 404 CHECK_NOT_FOUND when no check with that id belongs to this key’s account and mode. A sandbox key can’t read a live check, and the other way round.
  • Sandbox keys run the same flow with the test codes, so you can build your webhook handler before you spend a live check. A sandbox key your agent got without an account can poll, but can’t use callback_url until it is claimed.