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

# Create an SDK run

> Issue an SDK run from your backend and hand it to your app.

Your backend creates every SDK run with your Partner API key, then hands the run to your app. Your app never holds the API key.

## Create the run

Call the same endpoint as for an iframe session, with `"channel": "sdk"`:

```bash theme={null}
curl -X POST https://app.cr3dentials.xyz/v1/partner/browser-session \
  -H "x-api-key: $CR3D_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sdk",
    "platformId": 12,
    "externalReferenceId": "user_abc123"
  }'
```

**channel** `string` *required*

`sdk`. Without it you get an iframe session.

**platformId** `integer` *required*

A platform with an SDK pack. If the platform has none, the call fails with `400` and the message `Platform '<id>' has no SDK pack`.

**externalReferenceId** `string`

Your own reference for this run. It comes back in responses and webhooks.

**expiresInHours** `integer`

The run's deadline. Defaults to 6 hours for an SDK run. A run with no result by then ends in `ERROR` ([below](#when-a-run-never-reports)).

**receiverData** `object`

Optional details about your user, stored with the run.

The options that control the remote browser and notifications don't apply to SDK runs: `location`, `preferredRegion`, `notificationPlatformId`, `senderName` and `emailTemplateId`. The run happens on the user's own phone, on their own network.

> [!NOTE]
>
> If we've given you a **check** id for a verification, send `checkId` in place of `platformId`. Send `params` too if the check takes inputs: a JSON object of at most 16 KiB. The response's `packId` is then the check id. Pass `params` to your app as JSON text.

## The response

```json theme={null}
{
  "status": "success",
  "data": {
    "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "CREATED",
    "platformId": 12,
    "packId": "upwork-freelancer",
    "accessToken": "9c41…e07b",
    "resultUrl": "https://…/sdk-api/runs/a1b2c3d4-e5f6-7890-abcd-ef1234567890/result",
    "expiresAt": "2026-10-22T18:00:00.000Z",
    "createdAt": "2026-10-22T12:00:00.000Z",
    "externalReferenceId": "user_abc123"
  }
}
```

## Hand the run to your app

Return three fields to your app over your own authenticated API. The app passes them to `runTree`:

| Response field | `TreeRunConfig` field |
| - | - |
| `packId` | `platformId` |
| `resultUrl` | `resultUrl`, exactly as returned |
| `accessToken` | `resultToken` |
| your `params`, as JSON text | `paramsJson`, or `""` when there are none |

> [!WARNING]
>
> `accessToken` authorizes the result for this one run. Send it only to the user who is running it, and never log it.

Create a new run every time the user starts a verification. The SDK never retries, so an attempt that failed in the app is over. When the app reports a failure or a cancel, terminate the old run. Otherwise it waits until its deadline and fires an `ERROR` webhook:

```bash theme={null}
curl -X DELETE https://app.cr3dentials.xyz/v1/partner/browser-session/{sessionId} \
  -H "x-api-key: $CR3D_API_KEY"
```

Terminating a run revokes its token, so a result that arrives later is refused.

## Get the result

An SDK run stays `CREATED` until its result arrives. Then it moves straight to a terminal status:

| Status | Meaning |
| - | - |
| `COMPLETED` | The pack collected everything it reads. `extractedData` holds the result |
| `PARTIAL_COMPLETE` | A size cap ended a source before the platform reported it complete. `extractedData` holds what was collected |
| `ERROR` | The platform step failed, for example because the user never signed in. `errorMessage` gives the reason in the pack's words |
| `TERMINATED` | You terminated the run |

Use the same tools as for iframe sessions. A [webhook](/essentials/webhooks) fires on every terminal status. You can also poll `GET /partner/browser-session/{sessionId}`.

For a platform with attested data, the result can arrive after the user has closed your app. The attester posts it when it finishes reading the platform, so don't make the user wait in the app for the webhook.

### When a run never reports

A run with no result by its deadline moves to `ERROR` with `errorMessage: "run did not report"`, and your webhook fires. That can happen when the app was closed before the SDK could hand the run off, or when the phone lost its network. The deadline is `expiresInHours` if you set it, and 6 hours otherwise.

## What Cr3dentials checks

Before it stores a result, Cr3dentials checks:

* the run's token, and that the run is still waiting for a result;
* that the result is for the pack this run was issued for;
* for attested data, the attester's signed claim: the signature, the trust bundle it chains to, and the policy it was signed under;
* that the attester's receipt hasn't already been used by another result.

A result that fails any check is refused and nothing is stored. You don't need to verify anything yourself.


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