KYC Testing
This page pulls together everything you need to exercise the KYC surface from your backend against sandbox — the standard automated flow, address pre-check, manual retries, and the document-upload step-up flow (IDPV).
Sandbox base URL: https://isv-api.sandbox.prophetx.dev
What's testable from the ISV backend
| Surface | ISV backend calls | Sandbox behavior | Simulate specific outcomes? |
|---|---|---|---|
| Standard KYC | POST /private/v1/users | Real IDComply | Yes — send a documented test persona (below). Each has a deterministic outcome. |
| Manual retry | POST /private/v1/users/USERID/kyc-retry | Real IDComply | Yes — resubmit with a different persona. Per-user attempt cap applies. |
| Address pre-check | POST /private/v1/validate-address | Real Smarty | Yes — any US address; nonsense one triggers address_not_found. |
| Step-up / IDPV | GET /private/v1/tokens only (backend just mints the embed token) | Documents flow through ProphetX's manual review queue in sandbox | Yes — ProphetX reviewers can approve or reject arbitrary uploads on request. See §4. |
| Terms bundle | GET /private/v1/terms, POST /users/USERID/terms | Static per-environment | Yes — submit a stale totalVersion to see stale_terms. |
1. Standard KYC personas
Send one of these identity payloads to POST /users and you'll get the outcome in the table. The personas exercise every terminal state IDComply returns.
1.1 US personas
| First / last | DOB | SSN last 4 | Outcome | Note |
|---|---|---|---|---|
| Scarlett Crowe | 1970-02-14 | 6324 | SUCCESS | Full match — happy-path baseline. |
| Elissy Hardy | 1970-02-14 | 8324 | SUCCESS | Full match — second happy-path user. |
| Anita Day | 1978-12-30 | 6789 | FAILURE | "Failed to verify" — retryable via kyc-retry or IDPV. |
| Sweta Cherry | 1996-06-16 | 4469 | FAILURE | "Invalid address" — retryable. |
| Laura Hayes | 1992-05-04 | 6341 | OFAC | Terminal ineligibility. |
| Kenji Saito | 2000-10-04 | 3682 | MORTALITY | Terminal ineligibility. |
Copy-paste-ready payloads:
SUCCESS — Scarlett Crowe
{
"firstName": "Scarlett",
"lastName": "Crowe",
"dateOfBirth": "1970-02-14",
"ssnLastDigits": "6324",
"addressLine1": "463 Massachusetts Ave",
"city": "Cambridge",
"state": "MA",
"zip": "02139",
"countryCode": "US",
"emailVerifiedAt": "2024-01-01T00:00:00Z"
}FAILURE — Anita Day (use this to enter the step-up flow)
{
"firstName": "ANITA",
"lastName": "DAY",
"dateOfBirth": "1978-12-30",
"ssnLastDigits": "6789",
"addressLine1": "1 test",
"city": "test",
"state": "NY",
"zip": "10001",
"countryCode": "US",
"emailVerifiedAt": "2024-01-01T00:00:00Z"
}OFAC — Laura Hayes (terminal)
{
"firstName": "Laura",
"lastName": "Hayes",
"dateOfBirth": "1992-05-04",
"ssnLastDigits": "6341",
"addressLine1": "15 505 Fir St",
"city": "Port Benjamin",
"state": "MO",
"zip": "63101",
"countryCode": "US",
"emailVerifiedAt": "2024-01-01T00:00:00Z"
}MORTALITY — Kenji Saito (terminal)
{
"firstName": "Kenji",
"lastName": "Saito",
"dateOfBirth": "2000-10-04",
"ssnLastDigits": "3682",
"addressLine1": "14 404 Dogwood Dr",
"city": "New Sheilaport",
"state": "NM",
"zip": "87501",
"countryCode": "US",
"emailVerifiedAt": "2024-01-01T00:00:00Z"
}1.2 CA personas
| First / last | DOB | SSN last 4 | Outcome | Note |
|---|---|---|---|---|
| John Citizen | 1986-11-23 | 6789 | SUCCESS | Full match. |
| Rose MortalityAlert | 1991-02-25 | 7890 | MORTALITY | Terminal. |
| Maggie DobAlert | 1994-01-22 | 7890 | FAILURE | "DOB not available in source records" — retryable. |
| Roderick AgeBelowLegalAlert | 2012-06-24 | 7890 | HTTP 422 | Under-age — rejected at validation, no user is created. |
| Matthew ListPepOfac | 1952-10-09 | 7890 | PEP | Terminal. |
| Justin PoBoxON | 1988-10-10 | 7890 | FAILURE | "PO Box address detected" — retryable. |
| Brian PartialMatchExample | 1989-03-13 | 0021 | FAILURE | Partial-match demo (city + ZIP fail, other fields match). |
| Melissa AddressFraud | 1991-10-03 | 0012 | FAILURE | "Unknown issue" — retryable, catchall failure mode. |
Set countryCode: "CA" and use provincial state codes (ON, BC, QC, etc.). Full identity payloads for the CA set are available in the same CSV as the US set — ask if you need a specific one dropped in.
2. Entering the step-up (IDPV) flow
Any of the bold personas above resolve to kycStatus: FAILURE. That's the state that unlocks both post-failure paths:
POST /users/USERID/kyc-retry— for failures caused by incorrect identity fields (typo in SSN, wrong ZIP for the state). Called by your backend.- IDPV (document upload) — for failures where the fields were fine but the vendor needs document evidence. Runs entirely inside the embedded UI; your backend only mints the embed token that authorises it.
From the ISV backend's perspective, the two flows look nearly identical — they both leave the user in PENDING for a few seconds and then land on SUCCESS or FAILURE. What differs is where the work happens:
Retry path (ISV backend drives it end to end):
1. POST /users with Anita Day → 201 { id, sharedSecret, kycStatus: "PENDING" }
2. GET /users/USERID/kyc-status (poll) → { kycStatus: "FAILURE", failReason }
3. POST /users/USERID/kyc-retry → 200 { kycStatus: "PENDING" } (send new/corrected identity fields)
4. GET /users/USERID/kyc-status (poll) → { kycStatus: "SUCCESS" } (assuming the resubmitted persona resolves that way)
IDPV path (backend only mints a token; the UI does the rest):
1. POST /users with Anita Day → 201 { id, sharedSecret, kycStatus: "PENDING" }
2. GET /users/USERID/kyc-status (poll) → { kycStatus: "FAILURE", failReason }
3. GET /private/v1/tokens → 200 { token, ... } (hand this to your frontend)
4. (frontend / embed opens the IDPV modal; user uploads a real government ID)
5. GET /users/USERID/kyc-status (poll) → { kycStatus: "PENDING" } while ProphetX reviews
6. GET /users/USERID/kyc-status (poll) → { kycStatus: "SUCCESS" | "FAILURE" }
Your backend never calls the IDPV endpoints directly — they're inside the embedded UI's contract with ProphetX and aren't in the public swagger for a reason. GET /users/USERID/kyc-status is the single observable your backend needs.
failReason is a plain string and is populated on every non-SUCCESS status — display it as-is for the retry-vs-IDPV branching UI on your side.
3. Address pre-check (POST /validate-address)
POST /validate-address)The Smarty pre-check is US-only. To see the three possible status values:
| Test address | status |
|---|---|
Exact real address (e.g. 1 Santa Claus Ln, North Pole, AK 99705) | verified |
Slightly wrong casing / missing ZIP+4 / abbreviations (1 Sant Claus Ln, North Pole, AK 99705) | corrected — response's normalizedAddress shows the fix |
Real building with apartments, but no unit supplied (350 5th Ave, New York, NY 10118) | missing_secondary |
Nonsense (999 Fake St, Nowhere, XX 00000) | 422 address_not_found |
Nothing is persisted — call it as many times as you want.
4. IDPV (step-up) testing
Since your backend doesn't call the IDPV endpoints directly, testing from the ISV backend side is really about observing — driving the user into FAILURE, opening the embed, and watching kyc-status change once the review resolves.
4.1 What you can control in sandbox
Outcomes are controllable, but the mechanism isn't persona injection. In sandbox, every IDPV submission lands in ProphetX's manual review queue — the queue is what turns "user uploaded something" into a final SUCCESS or FAILURE. That means we can approve or reject basically any upload on request, so you can deterministically test both branches — you just coordinate with your ProphetX contact rather than send a magic persona.
Practical approach:
- Line up which user session you're about to test through IDPV (share the
userId). - Ping your ProphetX contact and tell them how you'd like the review to resolve — pass, reject-and-let-them-retry, or reject-terminally.
- Run the flow; the outcome you asked for lands on the next
kyc-statuspoll.
Real government-issued IDs are still required for the upload step itself — IDComply doesn't offer test documents, and there is no synthetic "test ID" that will pass without review intervention. Use a real driver's licence, passport, or state ID; the reviewer decides the outcome afterwards. If you don't want to upload a real ID, submit whatever you like (a scanned utility bill, an unreadable image) and have the reviewer resolve the session however you need — the upload is only there because the form requires something.
4.2 Lifecycle branches you can exercise
All of these are reachable from the ISV backend perspective by watching kyc-status:
| Branch | How to set it up |
|---|---|
| Eligible → hosted form opens | Create user with Anita Day. Once kyc-status is FAILURE, mint a token and open the embed on the frontend. |
| Verified via IDPV | Ask the reviewer to approve the submission. kyc-status transitions FAILURE → PENDING → SUCCESS. |
| Documents rejected, retryable | Ask the reviewer to reject the submission. kyc-status returns to FAILURE. The user can go through IDPV again (up to the attempt cap). |
| Terminal ineligibility | Create user with a MORTALITY / PEP / OFAC persona from §1. IDPV isn't offered — those statuses resolve at initial KYC. |
| Attempts exhausted | Run three IDPV cycles for one user with rejections. On the fourth attempt the embed will surface an attempts-exhausted terminal screen; your backend sees kyc-status sitting at FAILURE and any further IDPV attempts from the UI won't produce a new session. |
4.3 What's not visible to your backend
The IDPV request/response bodies are between the browser (or embed component) and ProphetX — they don't land on your backend. If you're debugging what the modal is doing, that's your frontend / embed integration's problem, not the ISV backend contract. From this doc's perspective the backend contract is exactly "mint the token, then poll kyc-status."
5. Manual retry (POST /users/USERID/kyc-retry)
POST /users/USERID/kyc-retry)Retry is the other branch after a FAILURE. Use it when the user's identity fields were wrong (typo, mistyped ZIP, wrong SSN); not when documents are the escalation.
Test patterns:
- Recoverable retry: create with
Anita Day, wait forFAILURE,kyc-retrywithScarlett Crowe's fields → nextkyc-statuspoll returnsSUCCESS. - Retry exhausted: run three consecutive retries with FAILURE-inducing personas. On the fourth,
kyc-retryreturns409 kyc_not_retryable. (Cap is per-user.) - Wrong-state retry: call
kyc-retryon a user whose status isSUCCESS,PENDING,MORTALITY,PEP, orOFAC— returns409 kyc_not_retryable. To disambiguate cap-exhausted vs wrong-state, pollGET /kyc-statusfirst.
6. End-to-end suggested test matrix
Cover these scenarios and you've exercised every branch that matters:
| Scenario | Setup | Expected end state (from your backend's view) |
|---|---|---|
| Happy path | Create with Scarlett Crowe | kycStatus: SUCCESS, wallet auto-created |
Retryable failure → recovered via kyc-retry | Create with Anita Day → kyc-retry with Scarlett Crowe's fields | kycStatus: SUCCESS after next poll |
| Retryable failure → recovered via IDPV | Create with Anita Day → mint token → user uploads via embed → reviewer approves | kycStatus: SUCCESS after next poll |
| Retryable failure → IDPV rejected → retry | Anita Day → embed IDPV → reviewer rejects → embed IDPV again → reviewer approves | kycStatus: FAILURE after first review, then SUCCESS after second |
| Attempts exhausted (IDPV) | Anita Day → three IDPV rejections | kycStatus remains FAILURE; further IDPV attempts show the attempts-exhausted terminal in the modal |
| Attempts exhausted (kyc-retry) | Anita Day → three kyc-retry calls with FAILURE personas | Fourth kyc-retry returns 409 kyc_not_retryable |
| Terminal ineligibility (US) | Laura Hayes (OFAC) or Kenji Saito (MORTALITY) | Terminal kycStatus; both kyc-retry and the IDPV modal refuse to start |
| Terminal ineligibility (CA) | Rose MortalityAlert or Matthew ListPepOfac | Same shape as US |
| Under-age (CA) | Roderick AgeBelowLegalAlert | 422 on POST /users; no user created |
| Address validation | Nonsense US address | 422 address_not_found on POST /validate-address |
| Stale terms | POST /users/USERID/terms with a low totalVersion | 409 stale_terms with currentTotalVersion in the body |
7. Gotchas
- Sandbox uses real IDComply and real Smarty for standard KYC. There's no per-ISV "test mode" toggle to flip — the personas are the mechanism.
- IDPV in sandbox goes through manual review. Coordinate the outcome you want with your ProphetX contact; don't guess or hope a bad upload will be rejected on OCR alone.
- Personas are safely reusable in sandbox. The identity-hash collision check that would normally prevent duplicate user creation is disabled for test personas — a fresh
POST /userswith the same persona returns201with a newidandsharedSecretevery time, not409 user_already_exists. This is intentional; it lets you run the same scenario over and over without a cleanup dance. (In production the collision check is active, so real users can't be duplicated.) - You can only
DELETEa user inSUCCESSstate. Sandbox test users that landed onFAILURE,MORTALITY,PEP, orOFAC(or that are stillPENDING) return409 user_pending_kyconDELETE /users/USERID, even thoughkyc-statusclearly shows they're not pending. In practice this means test users from FAILURE personas accumulate in sandbox — they're harmless, but there's no self-service way to clean them up. Ask your ProphetX contact if you need them purged. emailVerifiedAtis required onPOST /users. Any past UTC timestamp is fine for sandbox testing — the value isn't independently verified against your email system.- Attempt caps count IDPV and kyc-retry independently. You get up to 3 IDPV attempts and up to 3 kyc-retry attempts per user; hitting the cap on one doesn't lock out the other.
failReasonis populated on every non-SUCCESSstatus, not onlyFAILURE. Read it for MORTALITY / PEP / OFAC too — useful for support triage.
8. Where the source of truth lives
- KYC endpoints: User Onboarding
- Embed token minting and the IDPV widget: Embedded UI
- Error codes: Errors, Limits, and Configuration Reference
Updated 17 days ago
