Skip to main content
DocV and prefill are the two optional flows that sit around the core verification call. Prefill goes in front of it — the user gives a phone number and date of birth, and you submit prefilled PII instead of a typed form. DocV comes out of it — when Socure wants a government ID, POST /v1/kyc/start returns a docv object and the user finishes verification in a hosted flow you hand them. Neither one changes the four outcomes, and neither one makes the decision.
Before this page you need:
  • a working start call you can drive to a docv object — see /kyc#start-a-verification,
  • docv_eligible: true on your start request, or you will never see the DocV flow,
  • the kyc:write scope — see /connect#authentication,
  • a registered webhook, because the terminal outcome only arrives there — see /webhooks.

The DocV state machine

Set docv_eligible: true or you do not get this flow

Leaving docv_eligible unset does not skip document verification. It routes the same cases to manual review, 1–2 business days, with no user-facing step and no way for the user to unblock themselves. Set it on every start request in every environment.

The state machine

Undefined is not a state we support. See Gaps.

Every value you can see

decision, status and subStatus are informational. Log them; do not branch on them.
Not yet published. The complete decision and status enumerations are not published, and "In Review" proves the subStatus set is larger than the documented one. Write your parser so that an unknown value is a poll-and-wait, never an exception and never a rejection.

The docv object

Two handles come back: the hosted docv.url you send the user to, and docvTransactionToken, the transaction handle.
Whether docvTransactionToken sits inside the docv object or at the top level of the response is not documented. Read it from both positions until your integration lead confirms which one ships.

Integration points

sdkKey is always empty, which settles the client architecture: you cannot initialise a native Socure SDK. There is no key to initialise it with. Every platform opens the hosted URL.
Not yet published. There is no documented return-URL or deep-link callback on docv.url, so there is no supported way for the hosted flow to hand the user back to your app. Design your UI so the user returns on their own — a “I’ve finished uploading” screen in your app that polls — and confirm the current behaviour with your integration lead before you ship a native flow.

Detecting submission: the 14-second trap

GET /v1/kyc/status keeps returning the docv object for roughly 14 seconds after the user submits their documents. During that window the response is indistinguishable from “has not started”. Two failure modes fall out of this, and partners hit both:
  • Polling immediately after the user says they are done, seeing the docv object, and telling the user the upload failed.
  • Re-issuing or re-opening the flow because the docv object is still present, which sends a user who has already submitted back into document capture.
Detect submission like this:
  1. Treat your own front-end “finished” signal as a hint, not a fact. It tells you when to start polling, nothing more.
  2. Wait at least 14 seconds after that hint before you read anything into the docv object’s presence.
  3. Poll GET /v1/kyc/status on a fixed interval and require the docv object to be absent on two consecutive polls before you treat the flow as left behind.
  4. Treat kyc.approved / kyc.rejected as the only authoritative completion. There is no review webhook, so the intermediate states are poll-only.
Show the user a neutral “checking your documents” state for the whole window. Do not show success or failure inside it.

Gaps: be explicit with your product team

State these as known unknowns in your design review rather than discovering them in production.
  • No published TTL for docvTransactionToken or docv.url. You cannot tell a user how long their link is good for, and you cannot expire it in your own UI on a matching timer.
  • No documented way to re-issue a DocV session after a user abandons it. There is no re-KYC and no reset either, so an abandoned flow has no published recovery path.
  • An expired, un-submitted session has been observed returning decision: ACCEPT with status: CLOSED and an empty participantId. That is a bug, not a contract. Never treat an ACCEPT with an empty participantId as an approval — that is exactly the shape this bug produces, and it is also the shape a normal in-flight provisioning produces. Gate on the kyc.approved webhook and this bug cannot reach your users.
Not yet published. Confirm the DocV link lifetime and the re-issue path with your integration lead before you launch. Until then, keep a support route for “my ID upload link stopped working” that ends with a human.

Prefill

Prefill is not on by default; confirm with your integration lead that it is enabled for your firm before you build against it. Prefill is an optional step that sits in front of the standard flow: the user gives a phone number and date of birth, confirms a one-time code, and you receive prefilled PII to submit to POST /v1/kyc/start instead of asking them to type it.
Not yet published. The prefill endpoint paths, request field names, response field names and date formats are not published anywhere. The snippets below are correct in their auth, error handling and sequencing, and deliberately empty in the two request bodies and the response mapping. Get that schema from your integration lead before you build against it, and do not guess field names from the standard start payload — prefill is a different surface.

Where prefill sits in the flow

1

Collect phone and date of birth

Two fields, not a full form. This is the whole point of prefill: the user types the minimum, not their address.
2

Request the one-time code

You post the phone number and date of birth. The user receives a code by SMS.
3

Verify the code

You post the code. On success you receive prefilled PII for that person.
4

Show the user what you got, then submit

Present the prefilled values for confirmation and correction, then call POST /v1/kyc/start with them. Prefill does not verify anybody. The decision still comes from the standard flow and all four outcomes still apply.
Prefill is a data-entry shortcut. It changes your form, not your state machine — you still handle approved, rejected, DocV and manual review exactly as on /kyc#start-a-verification.

The SSN rule

The exchange account is derived from the end user’s SSN, and one SSN maps to exactly one exchange account platform-wide. That does not change under prefill: the national ID / SSN must still be present in the payload you send to POST /v1/kyc/start, so keep that field on your form even when every other field arrives prefilled.
Not yet published. Whether prefill takes the last four digits of the SSN as an input, returns them, or requires a last-four match before releasing PII is not published. Plan your form for the case where the user must still enter their full SSN, and confirm the rule with your integration lead before you build a last-four-only flow.

Running the prefill flow

OTP retries

No platform-enforced OTP limit is published — not a send cap, not a verification-attempt cap, not a code lifetime, and no statement of whether any of it is scoped per phone number, per user, per firm or globally. Do not read that as “unlimited”. Read it as “you own this, and you will be the one paying for the SMS”. Enforce your own budget in your own application, and make the numbers yours, not ours:
  • Cap sends per phone number over a rolling window, and make the resend button visibly disabled with a countdown.
  • Cap verification attempts per issued code, then force a fresh send.
  • Expire the code in your own UI on your own timer so the user sees a clear “code expired, send a new one” state rather than a generic failure.
  • After your cap is reached, fall back to the standard flow on /kyc#start-a-verification and let the user type their details. Prefill is optional; never let it become a dead end.
Not yet published. Ask your integration lead for the platform-side OTP limits and their scope before launch. A limit without a scope is worse than no limit, and an SMS loop that retries server-side is the failure mode this section exists to prevent.

OTP error cases

There is no published error catalogue for the OTP steps. In practice the cases you have to design a screen for are: wrong code, expired code, too many attempts, unreachable or non-mobile number, and a date of birth that does not match the phone number’s owner. You will be able to tell them apart only by inspecting the bodies you actually get back. Until the catalogue exists, treat the OTP steps defensively:
  • Log the full response body verbatim on every non-200, including the status code. That log is currently the only way either side can identify a new OTP error case.
  • Do not map an unknown error onto “wrong code”. A user who is told their code is wrong will retype it and burn your attempt budget on a failure that was never about the code.
  • Show one generic recoverable message plus a “enter your details instead” escape hatch, and keep the exact error in your logs.
Not yet published. Send your integration lead the verbatim bodies you collect. There is no published list to check them against, so your logs are the source.

What can go wrong

Next

Webhooks and sandbox