Skip to main content

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 needWhere
Create or revoke API keysAPI keys
Webhook URL and signing secretSettings → Webhook
Webhook delivery history, and re-queueing a failed deliveryWebhooks
Manual review queueReview
Credit balanceBilling

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.

  1. The requestId from the failing response — also available as the X-Request-Id response header. This is the single most useful thing you can send; it maps directly to our logs.
  2. The error code and full detail object, not a screenshot of a generic message.
  3. The HTTP method and path, including which key family you used.
  4. A verificationId if the problem concerns a specific verification.
  5. Timestamp with timezone, and whether it is reproducible or intermittent.
  6. 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/verdict model
  • 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