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

SurfaceISV backend callsSandbox behaviorSimulate specific outcomes?
Standard KYCPOST /private/v1/usersReal IDComplyYes — send a documented test persona (below). Each has a deterministic outcome.
Manual retryPOST /private/v1/users/USERID/kyc-retryReal IDComplyYes — resubmit with a different persona. Per-user attempt cap applies.
Address pre-checkPOST /private/v1/validate-addressReal SmartyYes — any US address; nonsense one triggers address_not_found.
Step-up / IDPVGET /private/v1/tokens only (backend just mints the embed token)Documents flow through ProphetX's manual review queue in sandboxYes — ProphetX reviewers can approve or reject arbitrary uploads on request. See §4.
Terms bundleGET /private/v1/terms, POST /users/USERID/termsStatic per-environmentYes — 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 / lastDOBSSN last 4OutcomeNote
Scarlett Crowe1970-02-146324SUCCESSFull match — happy-path baseline.
Elissy Hardy1970-02-148324SUCCESSFull match — second happy-path user.
Anita Day1978-12-306789FAILURE"Failed to verify" — retryable via kyc-retry or IDPV.
Sweta Cherry1996-06-164469FAILURE"Invalid address" — retryable.
Laura Hayes1992-05-046341OFACTerminal ineligibility.
Kenji Saito2000-10-043682MORTALITYTerminal 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 / lastDOBSSN last 4OutcomeNote
John Citizen1986-11-236789SUCCESSFull match.
Rose MortalityAlert1991-02-257890MORTALITYTerminal.
Maggie DobAlert1994-01-227890FAILURE"DOB not available in source records" — retryable.
Roderick AgeBelowLegalAlert2012-06-247890HTTP 422Under-age — rejected at validation, no user is created.
Matthew ListPepOfac1952-10-097890PEPTerminal.
Justin PoBoxON1988-10-107890FAILURE"PO Box address detected" — retryable.
Brian PartialMatchExample1989-03-130021FAILUREPartial-match demo (city + ZIP fail, other fields match).
Melissa AddressFraud1991-10-030012FAILURE"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)

The Smarty pre-check is US-only. To see the three possible status values:

Test addressstatus
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:

  1. Line up which user session you're about to test through IDPV (share the userId).
  2. Ping your ProphetX contact and tell them how you'd like the review to resolve — pass, reject-and-let-them-retry, or reject-terminally.
  3. Run the flow; the outcome you asked for lands on the next kyc-status poll.

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:

BranchHow to set it up
Eligible → hosted form opensCreate user with Anita Day. Once kyc-status is FAILURE, mint a token and open the embed on the frontend.
Verified via IDPVAsk the reviewer to approve the submission. kyc-status transitions FAILURE → PENDING → SUCCESS.
Documents rejected, retryableAsk the reviewer to reject the submission. kyc-status returns to FAILURE. The user can go through IDPV again (up to the attempt cap).
Terminal ineligibilityCreate user with a MORTALITY / PEP / OFAC persona from §1. IDPV isn't offered — those statuses resolve at initial KYC.
Attempts exhaustedRun 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)

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 for FAILURE, kyc-retry with Scarlett Crowe's fields → next kyc-status poll returns SUCCESS.
  • Retry exhausted: run three consecutive retries with FAILURE-inducing personas. On the fourth, kyc-retry returns 409 kyc_not_retryable. (Cap is per-user.)
  • Wrong-state retry: call kyc-retry on a user whose status is SUCCESS, PENDING, MORTALITY, PEP, or OFAC — returns 409 kyc_not_retryable. To disambiguate cap-exhausted vs wrong-state, poll GET /kyc-status first.

6. End-to-end suggested test matrix

Cover these scenarios and you've exercised every branch that matters:

ScenarioSetupExpected end state (from your backend's view)
Happy pathCreate with Scarlett CrowekycStatus: SUCCESS, wallet auto-created
Retryable failure → recovered via kyc-retryCreate with Anita Day → kyc-retry with Scarlett Crowe's fieldskycStatus: SUCCESS after next poll
Retryable failure → recovered via IDPVCreate with Anita Day → mint token → user uploads via embed → reviewer approveskycStatus: SUCCESS after next poll
Retryable failure → IDPV rejected → retryAnita Day → embed IDPV → reviewer rejects → embed IDPV again → reviewer approveskycStatus: FAILURE after first review, then SUCCESS after second
Attempts exhausted (IDPV)Anita Day → three IDPV rejectionskycStatus 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 personasFourth 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 ListPepOfacSame shape as US
Under-age (CA)Roderick AgeBelowLegalAlert422 on POST /users; no user created
Address validationNonsense US address422 address_not_found on POST /validate-address
Stale termsPOST /users/USERID/terms with a low totalVersion409 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 /users with the same persona returns 201 with a new id and sharedSecret every time, not 409 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 DELETE a user in SUCCESS state. Sandbox test users that landed on FAILURE, MORTALITY, PEP, or OFAC (or that are still PENDING) return 409 user_pending_kyc on DELETE /users/USERID, even though kyc-status clearly 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.
  • emailVerifiedAt is required on POST /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.
  • failReason is populated on every non-SUCCESS status, not only FAILURE. Read it for MORTALITY / PEP / OFAC too — useful for support triage.

8. Where the source of truth lives


Did this page help you?