> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sahlfinancial.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test in the sandbox

> Create a key, run six requests from this site, and check the result in the console. About 15 minutes.

You can run every endpoint from this documentation. The API playground sends the request from your browser straight to `https://app.sahlfinancial.com/api` with your own key. This site does not proxy the request and does not store your key.

<Warning>
  Use fake clients and fake documents only. Never upload a real client document.
</Warning>

## What you need

| Item | Where it comes from |
| - | - |
| A workspace enabled for the partner KYC API | Not self-serve: [request sandbox access](https://sahlfinancial.com/contact?type=demo). Without it the key form offers no `kyc:` scope and calls return 403 `kyc_scope_not_allowed`. |
| A console user who can manage keys | Role tenant admin or API manager, with a verified email. |
| An API key with the scopes you test | Created in the console, steps below. |
| A fake test file (optional, for `/extract`) | Any JPEG, PNG, WebP, TIFF or PDF you made yourself, up to 30 MB. |

## Step 1. Create a sandbox key

There is no separate sandbox key. A key works in both environments, and each call picks one with the `environment` field (`sandbox` by default). A "sandbox key" is a key you only use with `environment` set to `sandbox`.

1. Sign in at [app.sahlfinancial.com](https://app.sahlfinancial.com).
2. Open **Settings**, then the **API Keys** tab.
3. Click **New key**. Name it `sandbox-test`.
4. Tick the scopes you want to test: `kyc:extract`, `kyc:verify`, `kyc:eid`. Leave **Bound service account** empty. If you fill it in, every call must also send a Google identity token in `X-Partner-Identity`, which the playground cannot do.
5. Click **Create API Key**. The secret appears once, as `sk_` followed by 8 characters, an underscore and 64 characters. Copy it now. The console cannot show it again.

If you lose the secret, create another key and revoke the old one. See [Authentication](/authentication) for rotation.

## Step 2. Open the playground

1. Open the **API reference** tab of this site, then **Verify a profile**.
2. Click **Try it** at the top right of the page.
3. In the **Authorization** field paste your key. Paste the key only. The playground adds `Bearer`.
4. Leave the server as `https://app.sahlfinancial.com/api`.

The request body is already filled with a fake client. Pick the example named **Minimal profile, no documents** if the playground offers a choice.

<Note>
  The playground calls the API straight from your browser. If a request fails with a network error before any status code, your browser blocked it (CORS). Run the same request with cURL from the page's code sample, or use the [Postman collection](/postman).
</Note>

## Step 3. Run the six requests

Run them in this order. Each one is a few clicks.

### 3.1 Verify a profile

Send the minimal example as it is.

```json theme={"dark"}
{
  "reference": "client-0001",
  "environment": "sandbox",
  "subject": "Test Client",
  "kind": "individual",
  "values": { "first_name": "Test", "last_name": "Client" },
  "documents": [],
  "require_documents": false
}
```

Expected: HTTP 200 and a body shaped like this.

```json theme={"dark"}
{
  "passed": true,
  "checks": [
    { "id": "screening", "label": "Sanctions screening — no matches; PEP not list-screened", "severity": "info", "passed": true, "detail": "screened against 23000 sanctions entries. The bundle carries no PEP list, so politically-exposed status rests on the client's declaration, not on a list check." },
    { "id": "completeness", "label": "KYC/KYB data completeness (7%)", "severity": "warning", "passed": false, "detail": "missing 26 required data point(s): date_of_birth, citizenship, id_type, id_number, id_expiry, street1, city, province" }
  ],
  "critical_failures": [],
  "flags": [ { "id": "completeness", "severity": "warning", "passed": false } ],
  "completeness": { "required": 28, "present": 2, "percent": 7 },
  "policy": { "id": null, "version": 0, "source": "legacy", "regime": "none", "regulator": null, "purpose": "onboarding", "overrides_refused": [] },
  "registry": null,
  "case_id": "00000000-0000-4000-8000-000000000001"
}
```

The values differ in your answer: `case_id` is a real id, the entry count in the screening detail is the size of the loaded list, and your workspace policy may add checks. `passed: true` with a completeness warning is the normal result here. `flags` and `completeness.missing` are shortened above.

### 3.2 Break it on purpose

Set `require_documents` to `true` and send again. If your workspace policy allows it, the answer now has `passed: false` and a critical check `required:photo_id` with the detail `no readable government photo ID among the uploads`. This shows how a blocked file looks.

### 3.3 Read a document

1. Open **Read documents**.
2. Set `files` to your fake payslip. Set `doc_type` to `payslip`, `reference` to `client-0001`, `environment` to `sandbox`, `kind` to `individual`.
3. Send.

Expected: HTTP 200 with `fields`, `documents`, `field_count`, `checks`, `reader_unavailable`, `policy`, `case_id` and `document_ids`. A payslip gives no `checks`. Which fields come back depends on what is printed on your file; see [Document types and fields](/guides/document-types).

Each read counts against your monthly allowance (2,000 reads by default).

### 3.4 Assess risk

Open **Verify and assess risk** and pick the example **Profile with the suitability answers**. Send.

Expected: `verification`, `assessment` and `registry`. With the example values the assessment is risk profile 58 `Balanced`, capacity 20 `Low`, compliance risk `Low` or `Medium` (it depends on your policy and screening list) and a `suitability` string. See [Risk assessment](/guides/risk-assessment) for how each number is built.

### 3.5 Optional: start an eID check

<Warning>
  This call is real in sandbox too. The eID provider emails the client a PIN and a link as soon as the request is created. Use an address you control. In this version the client must be Canadian (`CA` or `CAN`), the only country the engine accepts, and your workspace needs its own eID provider account, otherwise the call returns 404 `identity verification is not set up for this tenant`.
</Warning>

Open **Start an eID check**, set your own email, send. You get HTTP 201 and a `key`. Then open **Get an eID check**, enter the `key` and send until `complete` is `true`. See [eID check](/guides/eid).

## Step 4. Look at the result in the console

Because every request carried a `reference`, the call filed a case on your workspace.

| Where in the console | What you see |
| - | - |
| **Cases** | One case per environment and reference: `client-0001` in `sandbox`. |
| **Documents** | The files you sent, read fields and per-document checks. |
| **Developers, Call log** | Every API call with its status, latency and `X-Request-ID`. |

The environment badge in the console top bar switches what the lists show. Switching to production works on every plan, within the plan's limits: the Free plan includes 10 Production cases a month and needs a verified work email (not Gmail or Yahoo), and Sandbox is unlimited on every plan.

## When it fails

| You see | Cause | Fix |
| - | - | - |
| 401 `Missing API key` | The Authorization field is empty. | Paste the key. |
| 401 `Invalid or revoked API key` | Typo, revoked key, or a key from another workspace. | Create a new key. |
| 403 `API key lacks the kyc:verify scope` | The key was created without that scope. | Create a key that has the scope. |
| 403 `kyc_scope_not_allowed` | The workspace is not enabled for the partner API. | [Request sandbox access](https://sahlfinancial.com/contact?type=demo). |
| 422 with a `loc` list | A field does not match the schema. | Read `detail[].loc` and `msg`. |
| 429 `kyc_extract_cap_reached` | The monthly read allowance is used. | Ask Sahl to raise it. |

All error bodies are in the [error catalogue](/errors).

## Next

<CardGroup cols={2}>
  <Card title="Postman" icon="paper-plane" href="/postman">Run the same requests with a collection and a test script.</Card>
  <Card title="End-to-end walkthrough" icon="route" href="/guides/walkthrough">A payslip and a Moroccan national ID to a risk assessment, with cURL, JavaScript and Python.</Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.