Support
Most integration problems fall into a small number of buckets, and each has a distinctive symptom. Work through this page first — it is faster than a ticket, and if it does not resolve things, the last section tells you exactly what to collect so the ticket can be answered in one round trip.
Self-diagnosis by symptom
401 on GET /v1/verify/:id, but the same key worked on /init
Read the error field before touching the key.
If it says secret_key_required, the key is valid — it is the wrong family. Results require a secret key (qv_sec_ / qv_sect_), because a publishable key is visible in your page source. Rotating the key will not help. Authentication.
If it says invalid_api_key, check the prefix. The test prefixes are qv_pubt_ and qv_sect_ — not qv_pub_test_. Also check that the key was not revoked; revocation takes effect immediately, with no grace period.
Every upload returns 400
Check detail.reason.
missing_upload_token means the client built its own headers instead of forwarding the ones /init returned. Uploads do not use your API key — they authenticate with X-Veridia-Upload-Token, which lives inside slot.headers. Pass that object verbatim. Uploading the images.
not_a_jpeg means the body is not a JPEG. PNG, HEIC and WebP are rejected — convert client-side.
bad_size means under 100 bytes or over 8 MB.
/submit returns doc_front_not_uploaded and you are sure you uploaded
The upload almost certainly failed and the failure was swallowed. Log the status and detail.reason of every PUT, then re-read the previous section.
429 even though traffic is low
You are hitting the per-IP limit (100 requests / 60 s), not the per-tenant one. It is checked before your API key is read, it covers the upload endpoint, and one active-liveness verification makes about 26 requests from a single device. Several mobile users behind one CGNAT address exhaust it while your tenant counters look idle. Rate limits.
Users who were approved are stuck pending — or rejected users got accounts
You are almost certainly branching on status instead of verdict.
status: "completed" means the pipeline ran. It does not mean the person passed; a rejection reaches completed too. Check both fields. GET /v1/verify/:id.
The mirror image of this bug: a threshold written as scores.faceMatch. The key is face_match, snake_case. In JavaScript the camelCase version is undefined, undefined < 70 is false, and the check silently never fires.
Webhooks are arriving but nothing happens
Check the field you switch on. The event type is type, not event. Handlers written against event fall through to the default branch, return 200 OK, and process nothing — so from our side delivery looks perfectly healthy. Webhooks.
The widget shows a generic error
The widget renders one generic message for most failures, so the on-screen text will not tell you which. Listen for the veridia:error event and read e.detail.code.
On a trial or exhausted account, the most common underlying cause is insufficient_credits (402), raised during authentication on /init.
Verification is failed
failed is not a rejection. It means the pipeline could not reach a conclusion, and there is no verdict to read. Treat it as retry-or-escalate, not as a decision about the applicant.
Where things live in the dashboard
| What you need | Where |
|---|---|
| Create or revoke API keys | API keys |
| Webhook URL and signing secret | Settings → Webhook |
| Webhook delivery history, and re-queueing a failed delivery | Webhooks |
| Manual review queue | Review |
| Credit balance | Billing |
Two notes that save tickets:
- The webhook secret is chosen by you, minimum 24 characters, and the field is write-only. There is no "copy it now, shown once" moment, and the dashboard will not display the current value. Store it where your application can read it, because you cannot recover it from us.
- There is one webhook endpoint per tenant, not a list of endpoints with per-event subscriptions. You receive all three event types or none.
Before you report something
Include these. Without them, the first reply is going to be a request for them.
- The
requestIdfrom the failing response — also available as theX-Request-Idresponse header. This is the single most useful thing you can send; it maps directly to our logs. - The
errorcode and fulldetailobject, not a screenshot of a generic message. - The HTTP method and path, including which key family you used.
- A
verificationIdif the problem concerns a specific verification. - Timestamp with timezone, and whether it is reproducible or intermittent.
- Test or live, and roughly what volume you were running.
Never send us an API key, a webhook signing secret, or a customer's document images. We do not need them, and we will ask you to rotate anything that is disclosed.
What we cannot do today
Stated plainly so you do not plan around it:
- We cannot raise rate limits for a single tenant. The limits are fixed in the deployment; there is no per-tenant override. Plan against the published numbers.
- There is no key rotation with a grace period. Create the new key, deploy it, verify traffic, then revoke the old one. Doing it in the other order is an outage.
- There is no published egress IP list for allowlisting our webhook deliveries.
- We cannot recover your webhook signing secret. You chose it; we store it to verify against, not to hand back.
Reference
- API Reference — endpoints, auth, and the two-axis
status/verdictmodel - Errors — every code, with which ones are worth retrying
- Rate limits — both limits, and the CGNAT arithmetic
- Webhooks — payload shape, signature verification, retries
- Compliance — what is implemented and what is not