Skip to main content
Use this flow when your application already performs identity capture and only wants Pods to handle the ramp provider side and expose one normalized status for money movement. You keep your own liveness and documentoscopy providers. You send Pods the evidence you already hold; Pods creates the ramp subaccount, uploads the images, submits the KYC, and tracks it to a verdict.
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

This call is safe to repeat. A CPF that already has a session under your account returns the existing 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.
A failed submit does not mean a failed KYC. This call does real work before responding — it creates the ramp subaccount and uploads all three images — and takes roughly 20 seconds, almost entirely image upload. The platform edge cuts the connection at 30 seconds and answers with its own HTML error page while the submission carries on and succeeds server-side, so a timeout, a 503 or a non-JSON body tells you nothing about the outcome.On any error or timeout, poll /status before concluding anything. Never re-POST /submit to “make sure” — a retry is safe, but it answers with the current state (provider_pending) rather than doing the work twice, so it cannot help you and it cannot tell you more than /status already does.
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:
Do not read that 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

The attestation 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.
Amazon Rekognition returns the reference image as bytes in the API responseReferenceImage.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.
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 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

Poll every 10 seconds until 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:
These are Pods codes, not the upstream provider’s. They stay stable if Pods changes ramp provider, so you can program against them safely — which 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 repeated POST /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.