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

# Importing Sumsub Tokens

> Generate Sumsub share tokens, redeem them on Dakota, and complete the resulting applications.

Once the [sharing relationship with Dakota is in place](/documentation/sumsub-token-sharing#before-you-start-establish-the-sharing-relationship), you can start moving customers across. This page covers the three steps: the donor generates a share token, you redeem it, and the application is completed.

<Info>
  New to this flow? Start with [Sumsub Token Sharing](/documentation/sumsub-token-sharing) — it covers how Reusable KYC works, what data transfers, and the one-time setup.
</Info>

# Step 1: The Donor Generates the Share Token

The donor generates a share token on their Sumsub account, scoped to Dakota's Sumsub `clientId`. This is a Sumsub-side operation — Dakota is not involved at this stage. The full request and response schema is documented in Sumsub's [Generate share token](https://docs.sumsub.com/reference/generate-share-token) reference.

A typical request looks like:

```bash cURL theme={null}
curl -X POST 'https://api.sumsub.com/resources/accessTokens/shareToken' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Token: <DONOR_SUMSUB_APP_TOKEN>' \
  -H 'X-App-Access-Sig: <signature>' \
  -H 'X-App-Access-Ts: <timestamp>' \
  -d '{
    "applicantId": "<applicant_id_on_donor_side>",
    "forClientId": "dakota.xyz_158913",
    "ttlInSecs": 1200
  }'
```

The response contains a `token` field. The donor sends this token to you out-of-band — typically via secure email, your dashboard, or a private API call.

<Info>
  **Dakota's Sumsub `clientId` is `dakota.xyz_158913`.** Pass this exact value as `forClientId` when generating the token — it scopes the token to Dakota, and Sumsub will reject the token if any other client tries to redeem it.
</Info>

<Info>
  **Relationship required first.** This request only succeeds once the donor has added Dakota as a sharing partner in Sumsub — see [Before You Start](/documentation/sumsub-token-sharing#before-you-start-establish-the-sharing-relationship).
</Info>

<Warning>
  **Tokens expire fast.** Sumsub share tokens default to a **1200-second (20-minute) TTL** and are single-use — once redeemed they cannot be reused. The donor can extend the TTL up to Sumsub's maximum via the `ttlInSecs` parameter, but the token will still expire. Plan to redeem tokens as soon as the donor hands them over; if a token expires before you import it, ask the donor to generate a new one. See the [Sumsub share-token reference](https://docs.sumsub.com/reference/generate-share-token) for the exact TTL bounds.
</Warning>

# Step 2: Redeem the Tokens on Dakota

Hand the share tokens off to Dakota to create the customers and applications. Customer names are derived automatically from each token's Sumsub data — you don't supply them.

There are two ways to redeem tokens. **The dashboard is the recommended path** for one-off migrations; the API exists for when you want to automate it.

## From the dashboard (CSV upload)

1. In the Dakota dashboard, open the **Customers** page and click **Add customer**.

2. Switch the toggle from **Manual** to **Sumsub Import**.

3. Click **Download template** to get a starter CSV. The format is one column called `token` with one share token per row:

   ```csv theme={null}
   token
   _act-sbx-jwt-paste-first-token-here
   _act-sbx-jwt-paste-second-token-here
   ```

4. Paste the tokens generated by the donor into the CSV (one per line), then upload the file.

5. Click **Import**. The dashboard parses the CSV client-side and submits the tokens. Per-row results — success, customer / application IDs, or the error message — are shown when the request finishes.

The dashboard upload calls the same `POST /customers/bulk-import-sumsub-tokens` endpoint described below, so the limits and behavior are identical.

## From the API

```http theme={null}
POST /customers/bulk-import-sumsub-tokens
```

```bash cURL theme={null}
curl -X POST 'https://api.platform.dakota.xyz/customers/bulk-import-sumsub-tokens' \
  -H 'X-API-Key: <YOUR_API_KEY>' \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{
    "tokens": [
      "_act-sbx-jwt-eyJhbGciOiJub25lIn0...",
      "_act-sbx-jwt-eyKhbGciOiJub25lIn0..."
    ]
  }'
```

**Limits:** between 1 and 100 tokens per request.

**Response:**

```json theme={null}
{
  "total": 2,
  "succeeded": 1,
  "failed": 1,
  "results": [
    {
      "name": "Sarah Williams",
      "success": true,
      "customer_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
      "application_id": "2hCjxJzUAW6JVRkZqaF9E0KpM3b"
    },
    {
      "name": "Unknown",
      "success": false,
      "error": "Share token expired"
    }
  ]
}
```

The bulk endpoint always returns `200`. Check `success` on each result individually — failed tokens don't roll back successful ones.

# Step 3: Complete the Onboarding

The import fills in everything the share token carried. The application is created in a **pending** state and **cannot be submitted until the remaining fields are supplied**.

Typically still missing:

* Employment status, purpose of account, source of wealth
* Attestations
* SSN, if Sumsub's TIN field didn't carry a properly formatted one

You can send the customer the application link and let them finish on Dakota, or supply the data over the API and keep them inside your own app.

<Card title="Completing an Imported Application" icon="list-check" href="/documentation/completing-an-imported-application">
  Both routes, step by step — getting an application token, sending the remaining fields, and submitting. The same flow whether the application came from Sumsub or Persona.
</Card>

<Info>
  **Address fallback:** When Sumsub doesn't return an address (rare, but possible for minimal verification levels), Dakota inserts a placeholder country code so the application stays loadable. The address will fail validation on submit, forcing the admin to enter the real one.
</Info>

# Error Handling

Token-level problems do **not** produce an HTTP error. The endpoint returns `200` and reports each token's outcome in its own `results[]` entry, so one bad token never affects the others — check `success` per row rather than branching on the status code.

## Per-token outcomes (HTTP `200`, reported in `results[].error`)

When Sumsub rejects a token, its own message is passed through verbatim as the row's `error` — so the exact wording comes from Sumsub, not Dakota.

| Scenario                                    | `error` value                                                                                           |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Invalid, expired, or already-redeemed token | Sumsub's own message (e.g. `Share token expired`)                                                       |
| Incompatible data (Sumsub error `10513`)    | Sumsub's own message — the donor's verification doesn't satisfy Dakota's level; no applicant is created |
| Sumsub rejected the token without a message | `Sumsub rejected this share token`                                                                      |
| Sumsub unreachable                          | `Could not reach Sumsub — please retry`                                                                 |
| Redemption failed for any other reason      | `Could not redeem share token`                                                                          |
| Nothing usable in the Sumsub payload        | `No data could be mapped from Sumsub`                                                                   |
| Applicant has no date of birth              | `Cannot import applicant without a date of birth (required for sanctions screening)`                    |
| Customer record could not be created        | `Failed to create customer: ...`                                                                        |
| Applicant data could not be saved           | `Failed to save: ...`                                                                                   |

## Request-level errors

| Scenario                                | Status | Detail                                                  |
| --------------------------------------- | ------ | ------------------------------------------------------- |
| Empty or missing `tokens` array         | `400`  | `Request must contain at least one token`               |
| Caller lacks the required permission    | `401`  | Token import requires the `create_customers` permission |
| `X-Idempotency-Key` is not a valid UUID | `400`  | `Invalid X-Idempotency-Key: must be a valid UUID`       |

If the redemption succeeds but the donor's documents don't fully satisfy Dakota's verification level, the application is still created and the import is reported as a success. The gap surfaces when the application is reviewed or submitted, and the customer will need to upload the missing documents through the standard flow.

# Things to Know

**Tokens are scoped to Dakota.** When the donor generates the token, they specify Dakota's Sumsub `clientId` as `forClientId`. Tokens cannot be redirected to other recipients.

**Tokens are single-use and time-limited.** Default TTL is 20 minutes. If a token expires, the donor must generate a new one — there's no extension.

**Verification level matters.** Dakota redeems tokens against its `id-only` verification level. If the donor's level required strictly more (e.g. enhanced due diligence), the extra data is ignored. If it required strictly less, the customer may end up in `documents_requested` status until the gap is closed.

**Permission.** Redeeming tokens requires the `create_customers` permission.

**Idempotency.** The endpoint honors the `X-Idempotency-Key` header, and the value **must be a valid UUID** — anything else is rejected with a `400`. Sending the same key twice with the same body returns the original result rather than redeeming the tokens a second time. Use a fresh key per logical operation.

**Sumsub applicant ID is recorded.** The new applicant created on Dakota's side is stored as `provider_applicant_id` on the individual applicant record, so you can correlate Dakota applications back to their Sumsub origin.

# What's Next

* Review the [Customer Onboarding](/documentation/customer-onboarding) guide for the standard onboarding flow that imported applications complete with.
* See the [API Reference](/api-reference/introduction) for the full request/response schema of `bulkImportFromSumsubTokens`.
* Subscribe to [Webhooks](/documentation/webhooks) to receive notifications when imported applications progress through review and approval.
