> Toden documentation. For the full index fetch /llms.txt. Every page is available as Markdown by appending .md to its URL. Identity verification uses /api/kyc, Risk Intelligence uses /api/v1; they are separate contracts with different error shapes. Never invent an endpoint, field or enum value: read the API reference page for the operation first.

# Quickstart: hosted verification link

> Create a hosted verification session with a secret key, complete it with a sandbox test ID, and read the outcome by webhook or status call.

This is the shortest route to a real verification result. Your backend asks Toden for a link, your customer opens it and verifies on a page we host, and your backend receives the outcome. There is no client code to write.

**You will need:** a Toden account, about five minutes, and a terminal with `curl`.

### Get a Sandbox secret key and a workflow ID

In the [console](https://console.toden.co), switch the environment selector to **Sandbox**.

1. Open **Developers → API Keys** and create a **Secret** key. It starts with `sk_test_` and is shown once, so copy it now.
2. Open **Workflows**. New organisations start with a published **Identity verification** workflow. If you do not see one, create an **Individual** workflow and publish it. Copy its ID, which starts with `wf_`.

```bash
export TODEN_SECRET_KEY="sk_test_..."   # your key, never committed
export TODEN_WORKFLOW_ID="wf_..."
```

> **Warning**
>
> A secret key reads identity data. Keep it on your backend. It must never appear in a browser, a mobile app or a repository.

### Create a session

A session is a verification link for one person. Pass your own reference for that person as `externalUserId`: it comes back on every result and webhook, so you never have to map IDs yourself.

```bash
curl "https://api.toden.co/api/kyc/sessions" \
  -H "Authorization: Bearer $TODEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"workflowId\": \"$TODEN_WORKFLOW_ID\",
    \"externalUserId\": \"user_42\"
  }"
```

The response is `201 Created`:

```json
{
  "session": {
    "sessionId": "cmt1gfjce05g547grqbmjsiqf",
    "url": "https://console.toden.co/verify/pXk3...",
    "shortCode": "7K9F-2QHM",
    "expiresAt": "2026-08-21T12:00:00.000Z",
    "kind": "individual",
    "externalUserId": "user_42",
    "workflow": { "id": "wf_AbC123dEf456", "name": "Identity verification", "version": 1 }
  },
  "delivery": null
}
```

Keep `session.sessionId`. It becomes the verification ID once the person submits, so the reference you hold now works for the whole lifecycle.

### Complete the verification as the customer

Open `session.url` in a browser, ideally on a phone. In Sandbox:

* Choose **Bank Verification Number (BVN)** if your workflow offers it, and enter the test ID `00000000001`. Sandbox accepts only published test IDs, and this one verifies.
* Complete the selfie step when asked.

Other test IDs produce other outcomes: `00000000002` is not found, `00000000004` is a selfie mismatch. The full list is in [Sandbox and test IDs](/get-started/sandbox-testing).

### Read the outcome

The status endpoint is safe to poll and returns no personal data:

```bash
curl "https://api.toden.co/api/kyc/status/cmt1gfjce05g547grqbmjsiqf" \
  -H "Authorization: Bearer $TODEN_SECRET_KEY"
```

Before the person submits you see the attempt (`"status": "in_progress"`). Afterwards you see the verification:

```json
{
  "verificationId": "cmt1gfjce05g547grqbmjsiqf",
  "status": "approved",
  "checkStatus": "verified",
  "externalUserId": "user_42",
  "workflowId": "wf_AbC123dEf456",
  "createdAt": "2026-08-20T12:00:00.000Z",
  "completedAt": "2026-08-20T12:00:05.000Z"
}
```

Branch on `status`. `approved` and `declined` are final; `in_review` means a person on your team needs to decide; `processing` means wait. [Verification lifecycle](/user-verification/concepts/verifications) explains every value.

### Read the full result on your backend

Names, the ID number and match results are only available with a secret key:

```bash
curl "https://api.toden.co/api/kyc/verifications/cmt1gfjce05g547grqbmjsiqf" \
  -H "Authorization: Bearer $TODEN_SECRET_KEY"
```

## What you should see

* The session call returns `201` with a `url`.
* After you finish the hosted flow, the status call returns `"status": "approved"` within a few seconds.
* The verification appears in the console under **Verifications**, in Sandbox.

If something else happens, [Troubleshooting](/get-started/troubleshooting) lists the common causes by error code.

## Replace polling with a webhook

Polling is fine for a first test. In a real integration, register a webhook endpoint under **Developers → Webhooks** and let Toden tell you when the outcome is ready.

1. Subscribe to the verification events. [Receive webhooks](/integrate/webhooks/overview) lists them.
2. [Verify the signature](/integrate/webhooks/signatures) on the raw request body before you trust the payload.
3. Store the event `id` and ignore repeats. Deliveries are retried, so the same event can arrive more than once.
4. Return `2xx` quickly, then do your own work.

## Next steps

#### [Create a session](/user-verification/server-api/create-session)

Prefill details, email the link for you, set metadata.

#### [Design the flow](/user-verification/workflows/workflows)

Countries, ID types, extra checks and decision rules.

#### [Open the link inside your app](/integrate/hosted-links/hosted-events)

WebView and iframe events, with nothing to install.

#### [Go live](/get-started/go-live)

The checklist for Production.