> ## 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 Persona Tokens

> Create Persona share tokens, import them into Dakota, and complete the resulting applications.

Once the [Connection with Dakota is active](/documentation/persona-token-sharing#before-you-start-establish-the-connection), you can start moving customers across. This page covers the three steps: the source creates a share token, you import it, and the application is completed.

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

# Step 1: The Source Creates the Share Token

The source organization creates a share token from a **completed** Inquiry, scoped to the Dakota connection. This is a Persona-side operation — Dakota is not involved at this stage. The full schema is in Persona's [Creating Share Tokens](https://docs.withpersona.com/creating-share-tokens) reference.

```bash cURL theme={null}
curl -X POST 'https://api.withpersona.com/api/v1/connect/share-tokens' \
  -H 'Authorization: Bearer <SOURCE_PERSONA_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "attributes": {
        "connection-id": "cxn_YourDakotaConnectionId",
        "source-id": "inq_CompletedInquiryId",
        "expires-in-seconds": 1209600
      }
    }
  }'
```

* **`connection-id`** — the `cxn_...` connection with Dakota you set up in [Before You Start](/documentation/persona-token-sharing#before-you-start-establish-the-connection).
* **`source-id`** — the record to share. For this flow it is a completed Inquiry (`inq_...`). Persona also supports Transaction (`txn_...`) and Account (`act_...`) source IDs, but Dakota's import expects identity from an Inquiry.
* **`expires-in-seconds`** — optional TTL. Persona allows **between 1 minute and 21 days**; when omitted, tokens **default to expiring 14 days after creation**.

The response contains the token, which uses the `cnst_` prefix (e.g. `cnst_ABC123...`). Send it to Dakota out-of-band — secure email, your dashboard, or a private API call.

<Info>
  **Dashboard alternative.** Source organizations can also create tokens in the Persona Dashboard under **Connect → Share Tokens** by selecting the Dakota connection and providing a source ID — handy for testing and one-off shares.
</Info>

<Info>
  **The Inquiry should be completed.** A share token from an incomplete Inquiry redeems, but the resulting application arrives sparse. Share tokens from completed, approved Inquiries carry the identity data and government ID Dakota imports.
</Info>

<Warning>
  **Tokens are time-limited.** Unlike some providers' 20-minute tokens, Persona share tokens default to a generous **14-day TTL** (up to 21 days) — comfortable for bulk migrations. They are still finite: a token that expires before Dakota imports it is reported as expired, and the source must create a new one.
</Warning>

# Step 2: Import the Tokens on Dakota

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

Because Persona redemption is asynchronous, Dakota's import is a **two-call flow**: submit the batch, then poll the job until it finishes.

## Submit the batch

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

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

**Limits:** between 1 and **5,000 tokens per request**. Split larger migrations into multiple batches — each returns its own job (a 15,000-token migration is three calls). Throughput is the same either way; see [Limitations](#limitations).

The response is immediate — no Persona call happens in the request path:

```json theme={null}
{
  "job_id": "pij_2b8XyZAbCdEf",
  "status": "running",
  "total": 2,
  "accepted": 2,
  "skipped_count": 0,
  "skipped": []
}
```

Some token *strings* can't be queued — a malformed token, a duplicate within the same batch, or a token already imported by a previous job. These are returned in `skipped[]` with an index and reason. A skipped token is only about the token string; it is **not** a decision about a person or application. Skipped tokens never block the accepted ones.

## Poll the job

```http theme={null}
GET /customers/persona-import-jobs/{job_id}
```

```bash cURL theme={null}
curl 'https://api.platform.dakota.xyz/customers/persona-import-jobs/pij_2b8XyZAbCdEf' \
  -H 'X-API-Key: <YOUR_API_KEY>'
```

```json theme={null}
{
  "job_id": "pij_2b8XyZAbCdEf",
  "status": "completed",
  "total": 2,
  "counts": { "succeeded": 2 },
  "results": [
    {
      "index": 0,
      "token": "cnst_...GpB",
      "state": "succeeded",
      "customer_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
      "application_id": "2hCjxJzUAW6JVRkZqaF9E0KpM3b"
    },
    {
      "index": 1,
      "token": "cnst_...Hqc",
      "state": "succeeded",
      "customer_id": "3PGKspCIc4dUgMWlGTHnaIreEQj",
      "application_id": "4iDkyKaVBX7KWSlareG0F1LqN4c"
    }
  ]
}
```

Each token advances through a state machine — `queued → inquiry_created → redeem_requested → redeemed → succeeded` — and every token is processed independently. Poll until `status` is terminal and each row's `state` is `succeeded` (or a terminal error state; see [Error Handling](#error-handling)). Because redemption is asynchronous, a row may sit at `redeemed` for a short time before it reaches `succeeded`.

<Info>
  **The import runs entirely server-side and survives interruptions.** The batch is persisted before any Persona call, so a closed tab, a refresh, or a Dakota deploy never loses progress — reopen the job to see current state. Dakota also paces itself against Persona's rate limits, so a large migration runs unattended.
</Info>

<Info>
  **Idempotency.** The submit endpoint honors `X-Idempotency-Key`. Resubmitting the same key returns the original `job_id` instead of starting a second import — safe to retry if a request times out.
</Info>

# 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
* Anything the source Inquiry didn't carry — proof of address, SSN

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 Persona or Sumsub.
</Card>

# Limitations

Know these before planning a large migration:

| Limitation                                                             | Detail                                                                                                                                                                                                                                                |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **5,000 tokens per request**                                           | Split larger migrations into multiple batches — each returns its own job. Throughput is unchanged (the pipeline is rate-limited regardless), and smaller jobs isolate failures better.                                                                |
| **Throughput ≈ 4–5 hours per 15,000 tokens**                           | Persona enforces a per-environment rate limit (≈300 requests/minute), and each token costs three Persona calls. Dakota paces itself under that limit, so a large migration runs steadily over hours — not instantly. Plan the window accordingly.     |
| **Redemption is asynchronous**                                         | Submit returns a job; poll it. A token finishes a short time after Persona reports it redeemed. Nothing is lost if you close the tab — the job runs server-side.                                                                                      |
| **Token TTL**                                                          | Share tokens expire per the TTL set at creation (default 14 days, max 21). Import them before they expire; an expired token must be re-created by the source.                                                                                         |
| **Identity data and documents transfer; Dakota-specific fields don't** | Employment, purpose of account and source of wealth are custom fields in Persona, so they can't be mapped automatically — send them via the API instead. See [Completing an Imported Application](/documentation/completing-an-imported-application). |
| **One import per token**                                               | A token already imported by a previous job is skipped as a duplicate. Safe to re-submit a batch after a partial failure.                                                                                                                              |
| **Individuals only**                                                   | Business onboarding is not supported by this flow.                                                                                                                                                                                                    |

None of these block a bulk migration — they shape *how* you run it (batch into 5,000s, budget several hours, import before tokens expire).

# Error Handling

There are two distinct kinds of outcome, and they are reported differently.

## Skipped at submit — the token string couldn't be queued

Returned in `skipped[]` on the submit response (the job still starts for the accepted tokens):

| Scenario                                 | `reason`                                   |
| ---------------------------------------- | ------------------------------------------ |
| Not a `cnst_...` token                   | `invalid token format (expected cnst_...)` |
| Same token twice in the batch            | `duplicate token in batch`                 |
| Token already imported by a previous job | `token already imported by a previous job` |

## Per-token errors — during background processing

A queued token that can't be completed ends in a **terminal state** in the job's `results[]`, with a reason. One bad token never blocks or rolls back the others.

| `state`     | Meaning                                                                                                                                                                                                                             |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `succeeded` | Customer and application created                                                                                                                                                                                                    |
| `expired`   | The share token expired before redemption — ask the source to create a new one                                                                                                                                                      |
| `failed`    | Redemption or mapping failed (e.g. Persona rejected the token, or the Inquiry had no usable identity data)                                                                                                                          |
| `stuck`     | The token redeemed, but the shared record never carried the identity data Dakota needs (typically a name or date of birth the source never collected). Terminal but actionable — ask the source to share a record with those fields |

## Request-level errors

| Scenario                        | Status | Detail                                            |
| ------------------------------- | ------ | ------------------------------------------------- |
| Empty or missing `tokens` array | `400`  | Request must contain at least one token           |
| No valid tokens in the request  | `400`  | No valid tokens in request                        |
| Caller lacks permission         | `401`  | Import requires the `create_customers` permission |

# Things to Know

**Tokens are scoped to Dakota.** A share token created for the Dakota connection can only be redeemed by Dakota — Persona rejects any other organization.

**Tokens are single-use on Dakota and time-limited.** A token already imported by a previous job is skipped as a duplicate, so you can safely re-submit an entire batch after a partial failure. Tokens expire per the TTL set at creation (default 14 days).

**Redemption is asynchronous.** Submit returns a job; poll it. This is a Persona characteristic — redemption completes on Persona's side and Dakota finalizes the customer when it does.

**Documents are copied, not linked.** The government-ID images are downloaded into Dakota's storage at import, so nothing depends on Persona's expiring file links afterward.

**Persona inquiry ID is recorded.** The imported application stores the Persona inquiry as `provider_applicant_id`, and `provider` is `persona`, so Persona- and Sumsub-sourced applications are distinguishable everywhere downstream.

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

# What's Next

* Review the [Customer Onboarding](/documentation/customer-onboarding) guide for the standard onboarding flow that imported applications complete with.
* Compare with [Sumsub Token Sharing](/documentation/sumsub-token-sharing) if you also reuse Sumsub verifications.
* Subscribe to [Webhooks](/documentation/webhooks) to receive notifications when imported applications progress through review and approval.
