Socious
Reference

Errors and status codes

Errors return a JSON body with an error field:

{ "error": "invalid apikey" }

Two responses carry more than that, and both are worth handling by their machine-readable field rather than their message text.

Status codes

CodeMeaningWhat to do
400Malformed request, unknown ID, or a rejected valueFix the request. An unknown UUID also lands here, not on 404
401Missing or invalid apikey or bearer tokenCheck the header. A key deleted in the dashboard fails this way
402Free plan — this action needs a paid oneSee below
403Authenticated, but not allowedNot a member of the owning organization, or a Premium+ schema from a non-Premium+ account
404No such policy or organization code
409State conflict — already on a paid plan, no billing account, voucher already redeemedNot retryable as-is
410A policy request token has expiredMint a new one
429Too many code-exchange attempts from one IPBack off. Only /vouchers/exchange is throttled
500Our sideSafe to retry
502An upstream service (Stripe, the wallet agent) failedRetry with backoff
503A feature is not configured on this deploymentNot retryable. Contact us

402 — upgrade required

{
  "error": "This action needs a paid plan. Contact our team to upgrade.",
  "code": "UPGRADE_REQUIRED",
  "tier": "FREE"
}

Branch on code. The message is written for a person and may change; UPGRADE_REQUIRED will not. See Plans, tiers and usage.

400 sql: no rows in result set

You will meet this one. It is what a lookup for an ID that does not exist returns — a wrong verification ID, a customer_id that was never opened against this verification, a credential from another account. Read it as “not found”, and check the ID before checking anything else.

A FAILED verification is not an error

GET /verifications/{individual_id}/verify returns 200 with status: "FAILED" when the person presented a credential that did not pass. The HTTP call worked. The answer was no. validation_error says why:

Message shapeCause
names an attribute and an operatorA condition was not satisfied
credential issuer … is not trusted by this verificationThe issuer is not in the trusted set
presented credential carries no issuer, and this verification pins its issuersThe issuer could not be identified, and this verification pins

Retrying

POST /verifications/individuals is idempotent per (customer_id, verification_id), so retrying it is safe. GET /verifications/{id}/connect is safe to call repeatedly — it returns the same link for 2 minutes, then mints a new one. Everything else that mutates (issuing, minting keys, minting codes) creates a new record on each call, so retry those only on a timeout, and check before you repeat.

Getting help

Contact us through the contact page. Include the verification ID and the individual ID, and the timestamp. With those we can trace one run end to end, which is much faster than reasoning from a status code.