---
title: "Simulate an outcome (test mode)"
method: POST
path: "/api/v2/kyc/simulate"
tags: ["Identity verification"]
---

# Simulate an outcome (test mode)

`POST /api/v2/kyc/simulate`

**Test mode only.** Drives a test-mode verification to a chosen terminal outcome instantly — test verifications never complete on their own. The simulated verdict flows through the same status contract and fires the same `identity.verification.updated` webhook a real review produces, so your status handling and webhook consumer are exercised end to end. Requires a test-mode client credential; live tokens get `403 sandbox_only`.

## Request body

- object
  - `user_id` string, required — The connected user's id.
  - `outcome` 'approved' | 'rejected' | 'requires_input', required — The verdict to apply. `approved` — verification succeeds. `rejected` — terminal rejection. `requires_input` — a retryable bounce asking for new document photos.
  - `reason` string — Optional end-user-safe explanation carried on non-approved outcomes — it appears as `reason` in statuses and webhook events, exactly like a real review's. It must not name internal providers or identifiers (rejected with `400 invalid_reason`), since it is shown to end users verbatim.

## Response `200`

The verification's new state, exactly as `GET /api/v2/kyc` now reports it.

- KycState — The single status contract every KYC response carries.
  - `object` 'kyc'
  - `status` 'awaiting_documents' | 'needs_information' | 'requires_verification' | 'pending' | 'approved' | 'rejected' — `awaiting_documents` — upload the front and back. `needs_information` — collect the `required_fields` and submit them. `requires_verification` — show the user the `iframe_url`. `pending` — under review, no action needed. `approved` — verified, done. `rejected` — the user did not pass. Statuses are not one-way: a review can send a user back — `pending` may return to `needs_information` (a detail didn't match the document; re-collect the listed fields and resubmit, the check re-runs automatically) or to `awaiting_documents` (the images were unusable; upload both sides again). Always branch on the current status.
  - `required_fields` string[] — Only on `needs_information` — exactly the fields to collect and post to `/kyc/information`.
  - `iframe_url` string — Only on `requires_verification` — the URL to show the user for the face scan. Embed it in an iframe with `allow="camera; microphone"`. Short-lived: always use the most recent one from a poll or webhook, never a stored copy.
  - `warnings` string[] — Optional, on document uploads — actionable feedback safe to show the user (for example, that the other side of the document is still needed).
  - `extracted` object — Optional, on document uploads — what the document reader pulled off the uploaded image(s), so you can prefill your details form instead of asking the user to re-type what the ID already says. Keys match the `/kyc/information` request fields (`first_name`, `last_name`, `date_of_birth`, `address_line1`, `address_city`, `address_region`, `address_postal_code`, `address_country`) plus `document_type`, `issuing_country`, and `document_number` (the number printed on the document — for US documents this is NOT the SSN, so never prefill it into `national_id_number` when `issuing_country` is `US`). Fields appear as they become readable: the front usually carries the name and date of birth; a US back adds the barcode address. Always let the user confirm or correct prefilled values.
  - `reason` string — Optional, on `needs_information`, `awaiting_documents`, `requires_verification`, and `rejected` — a short, end-user-safe explanation of what the review asked for (for example, “Enter your full name exactly as it appears on your identity document.”). Safe to show the user verbatim.

## Other responses

- `400` — `invalid_request` — missing `user_id` or an unknown `outcome`. `invalid_reason` — the `reason` names an internal provider or identifier (it is shown to end users verbatim).
- `401` — `unauthorized` — the platform access token is missing or expired. Exchange your client credentials for a fresh one.
- `403` — `sandbox_only` — the token is a live credential. Live verifications are decided by the identity provider and cannot be simulated.
- `404` — `connection_not_found` — no connection exists for that user under your client.
- `409` — `user_conflict` — the email on file in your organization belongs to a different account. Contact support.
- `502` — `verification_error` — the step failed downstream. Try again.

---

[API](https://skmtc.net/agentcard/apis/agentcard-api.md) · [All operations](https://skmtc.net/agentcard/apis/agentcard-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/agentcard/agentcard-api/versions/638d12c8303f/schema)
