Pods stores no images. They are held in memory for the duration of your request, forwarded to
the ramp provider, and discarded. This holds for both transports: an image sent as base64 and an
image Pods fetched from a URL you provided follow the same path — into memory, to the provider,
gone. Pods retains the ramp provider’s document ids, a SHA-256 digest, the liveness score, and
timestamps.The rest of the submitted payload is kept for up to 7 days so a failure on the provider’s side
can be retried without asking the applicant to capture again. On the
url transport that
includes the URL itself, signature and all — that is what makes the retry able to re-fetch the
bytes rather than just name them. Size X-Amz-Expires knowing Pods holds the link for that
window. See Retention.What you need before you start
The flow
1
Create the session
Send CPF, email and wallet address. You get back a
kycUserId — use it for every later call and
for status polling.2
Run your capture
Liveness and document capture happen entirely on your side, with your providers.
3
Submit the evidence
One call with everything: applicant data, address, the liveness image plus its attestation, and
the document images plus the data extracted from them.
4
Poll for the verdict
The ramp provider does not decide synchronously. Poll the status endpoint until the session
reaches a terminal state.
1. Create the session
kycUserId rather than creating a second one, with "existing": true. If that CPF is
already bound to a different email you get 409 KYC_PROFILE_EMAIL_MISMATCH — worth surfacing rather
than working around, since one person under two records is almost always a mistake.
2. Submit the evidence
One call. Every image is either inline base64 — as most capture providers return it — or an HTTPS URL that Pods fetches for you. Pick per image; see Image requirements.- Base64
- Presigned URL
timings reports each phase in milliseconds and is there to make a slow submission diagnosable
without a support ticket. It is informational; do not branch on it.
Rejections arrive here too
The ramp provider can refuse the submission during this call. When it does,/submit answers
400 with an error body, and the session moves to rejected or rejected_retryable:
message. Call the status endpoint and read the normalized reason instead — see
Handling rejections. The provider returns free-form text that changes
without notice; reason.code does not.
About the liveness attestation
Theattestation is your assertion about the check you ran. Pods records it and compares
confidence against the threshold configured for your account. Pods does not call your provider
to verify it — the record is stored as customer_attested, which is the honest description of the
arrangement.
Only two fields are load-bearing: status must be SUCCEEDED or PASSED, and confidence must
clear your threshold. Everything else is recorded for audit correlation and is not validated.
A confidence below the threshold is rejected with 422 LIVENESS_BELOW_THRESHOLD before any upload
happens, so you save the round trip and the provider call.
Coming from AWS Face Liveness
Coming from AWS Face Liveness
Amazon Rekognition returns the reference image as bytes in the API response —
B — bucket and pre-sign. Same bytes, stored first and submitted later. This is the right shape
when capture and submission are separate processes — a mobile capture that hands off to a backend
job, or a retry that must not depend on the Rekognition session still being readable.Do not bother setting
ReferenceImage.Bytes on GetFaceLivenessSessionResults — not as a durable URL. So the transport
you use depends on whether capture and submission are the same process. AuditImages carries the
other frames in the same shape; ReferenceImage is the one to submit.A — no storage needed. Read the session result and submit in one execution. The result is
readable only for a short window after the check, so fetch and submit together; there is nothing to
persist and no bucket to allowlist.response-content-type on the signed URL to match the object: Pods reads the
format from the bytes and ignores the header either way.The AWS calls above are illustrative. The field names were read from the published
@aws-sdk/client-rekognition type declarations.About the extracted document data
documents.attestation.extracted is the source of truth for the name, birth date and CPF sent to
the ramp provider — because that is what the provider compares the images against. Data typed by the
user tends to produce name and birth-date mismatches downstream. applicant.fullName and
applicant.dateOfBirth are optional and only fill gaps.
One rule is absolute: if extracted.cpf differs from the CPF the session was created with, the
request is refused with 409 IDENTITY_CPF_MISMATCH. Nothing is uploaded.
Image requirements
3. Poll the status
status is terminal.
The response carries the full profile; the fields above are the ones this flow adds. The rest is
shared with the other Pods KYC flows and documented in the API reference.
Handling rejections
When a session is rejected,reason is populated:
rejectReason, providerResult and
providerMessage on the same response cannot promise, since they carry raw provider values.
To retry, call
/submit again on the same kycUserId with corrected evidence for the block
named in retryFrom. Do not create a new session — deduplication is per CPF, so it will return the
same one anyway.
TAX_ID_ALREADY_VERIFIED means the person exists at the ramp provider under a different record.
A tax ID can only be verified once across the whole platform, so this is terminal for a new
record; reach out to Pods to have the existing identity attached to your account.Rejections before anything is uploaded
These are validation failures, returned in under a second. They never reach the ramp provider and never create a subaccount.Idempotency
There is no idempotency header. Deduplication is structural: one session per CPF per account. A repeatedPOST /sessions returns the existing session. A repeated /submit while an upload is in
flight, or after one completed, returns the current state instead of uploading the documents again.
Retention
The 7-day window exists for one reason: when the ramp provider fails on their side, Pods can retry
the submission instead of sending your applicant back through capture. Expiry is automatic and
needs no call from you.
Expiry is a floor, not an exact instant — records are swept periodically, so a payload may live
slightly past the 7-day mark. Nothing in the retry buffer is ever returned by an endpoint.
What Pods does not do
- Pods does not call your liveness or documentoscopy provider.
- Pods does not verify your attestation against your provider.
- Pods does not store images, ever.
- Pods does not keep the address or the full CPF beyond the retention window above.
- Pods does not return the liveness score. Only pass or fail — showing a biometric score to an end user is discouraged by the providers themselves.