An identity-verification check stores the images it worked from. GET /v1/sessions/{id}/idv-image/{artifact} streams one of them back to you, so your own product can show a person the face on the document instead of sending them to the console.
The response body is the image itself, not JSON.
The endpoint
Section titled “The endpoint”curl -s -o portrait.jpg \ https://api.us.checktiv.com/v1/sessions/vs_01H.../idv-image/portrait \ -H "Authorization: Bearer ah_sk_us_live_<your-key>"- Scope:
sessions:read_report, the same scope that authorizes the PDF report. A key without it gets403 insufficient_scope. Mint one that carries it from Developers → API keys → New API key in the console, or add it the next time you rotate. See Create an API key. - Response:
200,Content-Type: image/jpeg, and the raw bytes. Every image this endpoint serves is a JPEG. - Caching:
Cache-Control: no-store, private. The bytes are personal data about the applicant, so hold them only for as long as your own retention policy allows. - No shareable link. The API never issues a presigned or otherwise public URL for these images. If a browser in your product has to display one, proxy the request through your own authenticated backend: there is no URL you can put in an
<img src>that works without your secret key. - Rate limit: the
sessions:read_imageclass, 120 requests per minute on every plan tier. See Rate limits. - Region and mode: send the request to the endpoint that matches your key, as with every other call. A key cannot read a verification from the other region, and a test-mode key cannot read a live-mode verification. See Modes and regions.
The artifact values
Section titled “The artifact values”artifact |
What it is |
|---|---|
portrait |
The photograph of the document holder, cropped from the document image the applicant submitted. It is the smallest image that shows the face printed on the document, so it is usually the one you want to display. |
front |
The front of the identity document, as the applicant submitted it. |
selfie |
The applicant’s selfie or liveness frame, when the workflow ran a face check. |
That is also the order the API lists them in.
The back of a document is not served here. It is captured and stored for two-sided documents, and your reviewers see it in the console, but it carries dense printed and machine-readable personal data that a gate or check-in flow has no need for, so it stays inside the console. Asking for back, or for any value this API does not recognize, returns 404 resource_not_found and does not repeat the value you sent.
Ask which images exist before you fetch
Section titled “Ask which images exist before you fetch”GET /v1/sessions/{id} carries an idv object for every verification that ran an identity check:
{ "data": { "id": "vs_01H...", "status": "completed", "idv": { "capture_acquisition": "camera", "available_images": ["portrait", "front"] } }}available_images names exactly the artifacts this verification has stored bytes for. Read it first and request only what it lists. That is the difference between one fetch and one fetch per artifact with a 404 for each one that is not there, and it is the single most effective thing you can do to stay under the rate limit.
It never lists the back of a document, even for a two-sided document that captured one, because this API would refuse that request.
available_images is a locator, not a grant. It is on the retrieve response for any key with sessions:read, and it tells you only whether an image exists. Fetching the bytes still needs sessions:read_report.
The idv object is on GET /v1/sessions/{id} only. It is not on the list endpoint, not on the create response, and absent altogether from a verification that has not run an identity check, so read it defensively rather than assuming it is present.
When no image is stored: artifact_not_available
Section titled “When no image is stored: artifact_not_available”Expect this response and handle it as a normal outcome, not a fault. It is the most common thing this endpoint says about older verifications, and code that treats it as an error will report failures that are not failures.
A verification whose identity check ran before the document-holder portrait was produced has no portrait. The crop exists only if it was asked for at the moment the document was read, so nothing reconstructs it from what is already stored, and every verification recorded before this capability existed answers artifact_not_available for portrait.
That state is not permanent, though it is not a backfill either. Re-verifying the identity step reads the document again, and that run produces a crop like any other run, so available_images begins listing portrait for the verification once the new run completes. A re-verification asks the applicant for their document again and is capped per verification, so reach for it when a particular verification needs the crop rather than as a way to populate old records.
The same response covers three other situations: the run produced no usable face on the document, the workflow never reached that step, and the image has already been deleted under data retention or an erasure request.
What to do, in order:
- Do not retry. An image that was never produced will not appear later, and a deleted one does not come back.
- Fall back to another artifact from
available_images. For confirming a person against their document,frontshows the same printed photograph, uncropped. - If your workflow needs the portrait specifically for an older verification, ask the person to verify again. A new run produces one. See Start a verification.
Keep the two 404s apart. They mean opposite things:
| Response | Meaning | What to do |
|---|---|---|
404 resource_not_found |
Your key cannot see this verification id, or the artifact is not one this API serves. The response deliberately echoes nothing back, so it looks the same either way. |
Check the id, then check that the key’s region and mode match the verification. See Modes and regions. |
404 artifact_not_available |
Your key is authorized for this verification and the artifact is one we serve. There is simply nothing stored for it. | Fall back to another artifact, as above. Full guidance: artifact_not_available. |
Neither is retryable.
How the document arrived: capture_acquisition
Section titled “How the document arrived: capture_acquisition”idv.capture_acquisition records how the applicant supplied this run’s document images. It has three states, and the third one is not what most readers assume.
| Value | Meaning |
|---|---|
"camera" |
The applicant photographed the document with the device camera during the verification. |
"upload" |
The applicant supplied a photo of the document they already had. This is offered only when the workflow template turns on Allow photo upload. |
null |
This run was recorded before capture provenance was collected. It is a statement about the age of the run, nothing more. |
null does not mean camera, and it does not mean the source was unknown or lost. Every run the platform records now carries one of the two values, so a null you see is an old run and not a gap in a current one. Do not fold null into "camera" in your own code: that would report a provenance the platform never recorded.
The value is advisory, and it is not a control. It originates in the applicant’s browser, which means anyone able to tamper with the page can change it, exactly as with any other value a client declares. It is honest context for a person reviewing a verification. It is never an input to the verification outcome, and it never should be one in your integration either:
- It does not change the verdict. An uploaded document runs the same document validations, the same authenticity heuristics and the same face comparison as a photographed one, and it can pass automatically. The difference is recorded, not scored. The assurance tradeoff of turning photo upload on is described in Document upload.
- Do not gate anything of your own on it. Treating
"camera"as proof of a live capture would be trusting a value the browser declared. If you need a stronger signal about a real, present person, that is what thelivenessbiometric mode is for.
If you host the capture flow yourself with the Web SDK rather than using the managed journey, your submit callback receives the acquisition argument and has to forward it. Drop it and the run is recorded as a camera capture, which is correct until you turn photo upload on and then silently wrong. See Capture.
Billing and account state
Section titled “Billing and account state”These reads go through the same account-entitlement check as the rest of /v1, even though fetching an image is not itself billable. Only a small set of account-state reads is exempt, and this route is not one of them, so evidence you have already collected is not retrievable while the account cannot spend:
402 payment_requiredwhen the wallet balance is insufficient, or the wallet is locked or absent. Top up or turn auto recharge back on, then retry. See Wallet balance too low and Auto recharge.403 subscription_requiredwhen the plan was canceled or has passed the payment grace period. Restart the plan, then retry. See Subscription.
If this endpoint sits in an operational path where a billing lapse would be disruptive, keep auto recharge enabled and set the threshold above your busiest day. That, rather than retry logic, is what keeps the path open.
Every fetch is recorded
Section titled “Every fetch is recorded”Every request that reaches one of your verifications writes one entry to your organization’s activity log, whether it served an image or refused. A request turned away before that point is not logged: an image type we do not serve, a malformed verification id, or an id that is not yours are all refused before the verification is read, so they leave no entry. The entry records the action (kyc.idv.evidence_exported on a successful export, kyc.idv.evidence_export_failed when an authorized request served nothing), the artifact requested, and the outcome. It carries nothing about the applicant and no image data. See Activity log.
This is also why there is no shareable URL. The only way to fetch these bytes is with a secret key you issued and can revoke at any time, so an export is never anonymous. See Rotate or revoke an API key.
Related pages
Section titled “Related pages”- Rate limits for the
sessions:read_imageclass and what to do about a 429. - Choosing scopes for which scopes an integration like this needs.
- Document upload for what Allow photo upload does and when to turn it on.
- Capture for the
acquisitionargument on a self-hosted capture flow. - API error codes and the interactive reference for the full wire contract.