# Build with AI Source: https://docs.deck.co/ai Skill and context prompt for building Deck integrations with AI coding agents. AI coding tools write better Deck integration code when they have the right context. This page provides a skill and system prompt you can load into your tool of choice so it understands the Deck v2 API, follows correct patterns, and avoids common mistakes. ## Deck skill The Deck skill is a [SKILL.md](https://docs.deck.co/skill.md) file your agent can load to learn how to build Deck integrations. It bundles a product summary, API quick reference, decision rules, common gotchas, and a verification checklist. Download it from [https://docs.deck.co/skill.md](https://docs.deck.co/skill.md) and drop it in your agent's skills directory. ## Prompt If you'd rather use a prompt than the skill, paste the prompt below into your project's context file so it loads in every session. Common locations: | Tool | How to load | | ------------------ | -------------------------------------------------------------------------------------- | | **Cursor** | Add to your [project rules](https://cursor.com/docs/context/rules) in `.cursor/rules/` | | **Claude Code** | Add to your `CLAUDE.md` file in the project root | | **Codex** | Add to your `AGENTS.md` file in the project root | | **GitHub Copilot** | Save to `.github/copilot-instructions.md` | | **Other** | Paste into your tool's system prompt or project context | Copy the full prompt below, or combine sections that are relevant to your integration. ````markdown theme={null} # Deck v2 API Context Deck is a platform that connects to external websites on behalf of your users. You make REST API calls, Deck handles authentication, navigation, and data extraction, and returns structured JSON results. You never interact with websites directly. ## API Basics - Base URL: `https://api.deck.co/v2` - Auth: `Authorization: Bearer sk_live_...` - Content type: `application/json` - All work is asynchronous. API calls return immediately. Results arrive via events. - There is no sandbox or test environment. All calls are live. ## Core Integration Flow 1. Create a **Source** (an external website, defined by type and URL) 2. Store a **Credential** (encrypted auth details for a user on a source) 3. Create a **Task** (defines what to do on the source) 4. Run the **Task** with a credential (Deck creates a session, authenticates, and executes) 5. Receive results via **Events** pushed to your webhook or queue ## Resource ID Prefixes Every resource ID is prefixed by type: | Prefix | Resource | | --- | --- | | `agt_` | Agent | | `src_` | Source | | `cred_` | Credential | | `sess_` | Session | | `task_` | Task | | `trun_` | Task Run | | `stor_` | Storage Item | | `evt_` | Event | | `evtd_` | Event Destination | | `edlv_` | Event Delivery | | `req_` | Request ID | ## Key Endpoints | Action | Method | Path | | --- | --- | --- | | Create source | POST | `/v2/sources` | | Store credential | POST | `/v2/credentials` | | Delete credential | DELETE | `/v2/credentials/{credential_id}` | | Create task | POST | `/v2/tasks` | | Run task | POST | `/v2/tasks/{task_id}/run` | | Submit interaction | POST | `/v2/task-runs/{run_id}/interaction` | | List task runs | GET | `/v2/task-runs` | | Get task run | GET | `/v2/task-runs/{run_id}?include=input,storage,screenshots` | | Create event destination | POST | `/v2/event-destinations` | ## Credential Authentication Credentials support two auth methods: ```json { "source_id": "src_...", "auth_method": "username_password", "auth_credentials": { "username": "...", "password": "..." } } ``` Valid `auth_method` values: `username_password`, `none`. ## Credential Statuses `unverified` → `verified` → (optionally `invalid`) → `deleted` - `unverified` means stored but not yet used in a successful authentication - `verified` means successfully authenticated at least once - `invalid` means the source rejected the credentials - `deleted` means permanently removed from the vault ## Session Statuses Sessions are created automatically when a task runs: `queued` → `running` → `idle` (no active tasks) → `completing` → `completed` | `failed` You can reuse a session by passing `session_id` when running a task. ## Interactions When a task run needs user input (MFA, security question, account selection), status becomes `interaction_required`. The response includes an `interaction` object with `type`, `message`, and `fields`. Each field has a `name`, `type`, and `label`. Submit the response to: - Task runs: `POST /v2/task-runs/{run_id}/interaction` Body: `{ "input": { "": "" } }` ## Events and Webhooks Deck pushes events to destinations you configure (webhooks, AWS SQS, Kinesis, S3, GCP Pub/Sub, Azure Service Bus, RabbitMQ, Hookdeck). Event format: ```json { "id": "evt_...", "type": "task_run.completed", "data": { ... }, "created_at": "2025-01-15T09:30:00Z" } ``` Key event types: - `credential.verified`, `credential.invalid`, `credential.deleted` - `session.queued`, `session.running`, `session.idle`, `session.completing`, `session.completed`, `session.failed` - `task_run.queued`, `task_run.running`, `task_run.pending`, `task_run.completed`, `task_run.failed` - `task_run.interaction_required` Webhook signatures follow the Standard Webhooks specification. Use the `standardwebhooks` SDK to verify with your `whsec_` signing secret. ## Pagination All list endpoints use cursor-based pagination: - Query params: `limit` (default 20, max 100), `cursor` - Response: `{ "data": [...], "has_more": true, "next_cursor": "eyJ..." }` - Loop until `has_more` is `false` ## Idempotency All POST, PATCH, and DELETE endpoints accept an `Idempotency-Key` header (max 256 chars, expires after 24h). Same key + same params returns the original response. Same key + different params returns HTTP 409. ## Error Format ```json { "errors": [{ "type": "request", "code": "input_missing", "field": "name", "message": "..." }], "request_id": "req_..." } ``` Always branch on `type` and `code`, never on `message`. Always log `request_id` for debugging. Common error codes: - `rate_limit_exceeded` (429) - back off with exponential delay - `auth_invalid` (401) - credentials are wrong - `interaction_timeout` (408) - interaction expired, resubmit the task run - `input_missing` (400) - check the `field` property - `source_not_available` (503) - external website is down, retry later - `idempotency_error` (409) - same key with different params ## CRITICAL RULES FOR AI MODELS ### Always do 1. Use event destinations (webhooks, queues) to receive results. Never poll. 2. Use `Idempotency-Key` on all create requests. 3. Handle `interaction_required` status on task runs. 4. Paginate with `cursor` and `has_more`, never offsets. 5. Verify webhook signatures using the Standard Webhooks SDK. 6. Handle errors by `type` and `code`, never by `message`. 7. Write task prompts as high-level, source-agnostic goals. A task runs across every source that supports it, so never reference a specific website or UI. The prompt is combined with Deck's agent harness, which handles navigation, authentication, and tool use. Example: "Log in and fetch the latest bill." not "Click the sign-in button, enter credentials, navigate to billing..." 8. Design task input schemas with only the fields that change between runs. Keep them source-agnostic. Do not add fields tied to a specific source. 9. Design task output schemas with generic fields any supporting source can populate. Fields return `null` when a source doesn't have that data. Do not mirror a single source's data model. ### Never do 1. Do not poll `/v2/task-runs/{id}` in a loop. Use events. 2. Do not hardcode source IDs. Create sources via the API or look them up. 3. Do not ignore `interaction_required`. Users may need to complete MFA. 4. Do not assume task runs complete synchronously. They are always async. 5. Do not send credentials from the client. Always proxy through your server. 6. Do not branch on error `message` strings. They can change without notice. 7. Do not include detailed navigation steps in task prompts. They conflict with the agent harness and can restrict tools the agent needs. 8. Do not add source-specific fields to task schemas. A task must work across all sources that support it. ```` ## Raw files Point your AI assistant at these URLs to give it canonical source data: | File | Purpose | | ---------------------------------------------------------------------- | ------------------------------------------------- | | [`/api-reference/v2.json`](https://docs.deck.co/api-reference/v2.json) | OpenAPI 3.1 spec for the Deck v2 API | | [`/llms.txt`](https://docs.deck.co/llms.txt) | Index of all docs pages, formatted for LLMs | | [`/llms-full.txt`](https://docs.deck.co/llms-full.txt) | Full docs content concatenated into a single file | # Create an agent Source: https://docs.deck.co/api-reference/agents/create-an-agent /api-reference/v2.json post /agents Creates a new agent. An agent is an automation scoped to a use case that organizes related tasks. # Delete an agent Source: https://docs.deck.co/api-reference/agents/delete-an-agent /api-reference/v2.json delete /agents/{agent_id} Permanently deletes an agent. # List agents Source: https://docs.deck.co/api-reference/agents/list-agents /api-reference/v2.json get /agents Returns a paginated list of agents in your organization. # Retrieve an agent Source: https://docs.deck.co/api-reference/agents/retrieve-an-agent /api-reference/v2.json get /agents/{agent_id} Returns the full agent object including its associated tasks. # Update an agent Source: https://docs.deck.co/api-reference/agents/update-an-agent /api-reference/v2.json patch /agents/{agent_id} Updates an agent's name or description. Only the fields you include in the request body are updated. # Create a session token Source: https://docs.deck.co/api-reference/components/create-a-session-token /api-reference/v2.json post /token Creates a short-lived session token for the Auth Component. # Create a credential Source: https://docs.deck.co/api-reference/credentials/create-a-credential /api-reference/v2.json post /credentials Stores a new credential for authenticating with an external source. The credential is created immediately and can be used on task runs. # Delete a credential Source: https://docs.deck.co/api-reference/credentials/delete-a-credential /api-reference/v2.json delete /credentials/{credential_id} Permanently deletes a credential. # List credentials Source: https://docs.deck.co/api-reference/credentials/list-credentials /api-reference/v2.json get /credentials Returns a paginated list of credentials. Supports filtering by source, status, and external ID. # Retrieve a credential Source: https://docs.deck.co/api-reference/credentials/retrieve-a-credential /api-reference/v2.json get /credentials/{credential_id} Returns a single credential by ID. # Update a credential Source: https://docs.deck.co/api-reference/credentials/update-a-credential /api-reference/v2.json patch /credentials/{credential_id} Updates a credential's authentication details or external ID. Only the fields you include in the request body are updated. # Create an event destination Source: https://docs.deck.co/api-reference/event-destinations/create-an-event-destination /api-reference/v2.json post /event-destinations Creates a new event destination (e.g. a webhook endpoint) to receive platform events. # Delete an event destination Source: https://docs.deck.co/api-reference/event-destinations/delete-an-event-destination /api-reference/v2.json delete /event-destinations/{destination_id} Permanently deletes an event destination. # List event deliveries Source: https://docs.deck.co/api-reference/event-destinations/list-event-deliveries /api-reference/v2.json get /event-destinations/{destination_id}/event-deliveries Returns a paginated list of event deliveries for a destination. Supports filtering by delivery status. # List event destinations Source: https://docs.deck.co/api-reference/event-destinations/list-event-destinations /api-reference/v2.json get /event-destinations Returns a paginated list of event destinations. # Retrieve an event destination Source: https://docs.deck.co/api-reference/event-destinations/retrieve-an-event-destination /api-reference/v2.json get /event-destinations/{destination_id} Returns a single event destination by ID. # Update an event destination Source: https://docs.deck.co/api-reference/event-destinations/update-an-event-destination /api-reference/v2.json patch /event-destinations/{destination_id} Updates an event destination's name, status, or subscribed events. Only the fields you include in the request body are updated. # List event types Source: https://docs.deck.co/api-reference/events/list-event-types /api-reference/v2.json get /event-types Returns a list of all available event types you can subscribe to. # List events Source: https://docs.deck.co/api-reference/events/list-events /api-reference/v2.json get /events Returns a paginated list of events. Supports filtering by event type. # Retrieve an event Source: https://docs.deck.co/api-reference/events/retrieve-an-event /api-reference/v2.json get /events/{event_id} Returns a single event by ID, including its full data payload. # End a session Source: https://docs.deck.co/api-reference/sessions/end-a-session /api-reference/v2.json post /sessions/{session_id}/end Ends an active session. # List sessions Source: https://docs.deck.co/api-reference/sessions/list-sessions /api-reference/v2.json get /sessions Returns a paginated list of sessions. # Retrieve a session Source: https://docs.deck.co/api-reference/sessions/retrieve-a-session /api-reference/v2.json get /sessions/{session_id} Returns a single session by ID, including the task runs executed within it. # Start a session Source: https://docs.deck.co/api-reference/sessions/start-a-session /api-reference/v2.json post /sessions Starts a new isolated compute session for executing tasks. # Create a source Source: https://docs.deck.co/api-reference/sources/create-a-source /api-reference/v2.json post /sources Registers a new source. A source is a website or service that your agents connect to. # Delete a source Source: https://docs.deck.co/api-reference/sources/delete-a-source /api-reference/v2.json delete /sources/{source_id} Permanently deletes a source. # List sources Source: https://docs.deck.co/api-reference/sources/list-sources /api-reference/v2.json get /sources Returns a paginated list of sources in your organization. # Retrieve a source Source: https://docs.deck.co/api-reference/sources/retrieve-a-source /api-reference/v2.json get /sources/{source_id} Returns a single source by ID. # Update a source Source: https://docs.deck.co/api-reference/sources/update-a-source /api-reference/v2.json patch /sources/{source_id} Updates a source's name or URL. Only the fields you include in the request body are updated. # Retrieve a storage item Source: https://docs.deck.co/api-reference/storage/retrieve-a-storage-item /api-reference/v2.json get /storage/{storage_id} Returns a single storage item by ID, including a signed download URL for the file. # Cancel a task run Source: https://docs.deck.co/api-reference/task-runs/cancel-a-task-run /api-reference/v2.json post /task-runs/{run_id}/cancel Cancels a running or queued task run. # List task run screenshots Source: https://docs.deck.co/api-reference/task-runs/list-task-run-screenshots /api-reference/v2.json get /task-runs/{run_id}/screenshots Returns a paginated list of agent screenshots captured during a task run, oldest first. Each item includes a pre-signed download URL and the agent's reasoning at the time of capture. # List task run storage items Source: https://docs.deck.co/api-reference/task-runs/list-task-run-storage-items /api-reference/v2.json get /task-runs/{run_id}/storage Returns a paginated list of storage items (files) produced by a task run. # List task runs Source: https://docs.deck.co/api-reference/task-runs/list-task-runs /api-reference/v2.json get /task-runs Returns a paginated list of task runs. Supports filtering by agent, credential, source, task, workflow, status, result, and date range. # Retrieve a task run Source: https://docs.deck.co/api-reference/task-runs/retrieve-a-task-run /api-reference/v2.json get /task-runs/{run_id} Returns a single task run by ID, including its status, input, output, errors, and interaction state. # Submit interaction input Source: https://docs.deck.co/api-reference/task-runs/submit-interaction-input /api-reference/v2.json post /task-runs/{run_id}/interaction Submits user input to resume a task run that is paused waiting for interaction (e.g. MFA code, security question). # Create a task Source: https://docs.deck.co/api-reference/tasks/create-a-task /api-reference/v2.json post /tasks Creates a new task on an agent. A task is a defined action an agent can perform, such as fetching data or submitting a form. # Delete a task Source: https://docs.deck.co/api-reference/tasks/delete-a-task /api-reference/v2.json delete /tasks/{task_id} Permanently deletes a task. # List tasks Source: https://docs.deck.co/api-reference/tasks/list-tasks /api-reference/v2.json get /tasks Returns a paginated list of tasks. Supports filtering by agent and status. # Reset a task Source: https://docs.deck.co/api-reference/tasks/reset-a-task /api-reference/v2.json post /tasks/{task_id}/reset Resets a task's status to `learning`. The next run re-learns the flow from scratch. # Retrieve a task Source: https://docs.deck.co/api-reference/tasks/retrieve-a-task /api-reference/v2.json get /tasks/{task_id} Returns a single task by ID, including its input/output schemas and storage configuration. # Run a task Source: https://docs.deck.co/api-reference/tasks/run-a-task /api-reference/v2.json post /tasks/{task_id}/run Executes a task, creating a new task run. The run executes asynchronously. Use events or poll the task run endpoint to track progress. # Update a task Source: https://docs.deck.co/api-reference/tasks/update-a-task /api-reference/v2.json patch /tasks/{task_id} Updates a task's name, schemas, prompt, or storage configuration. Only the fields you include in the request body are updated. # Get test Source: https://docs.deck.co/api-reference/test/get-test /api-reference/v2.json get /test Verifies your API key is valid. # API status and changes Source: https://docs.deck.co/api/api-status-and-changes Versioning, breaking changes, and how Deck communicates platform updates. ## Platform status Deck publishes real-time operational status at [status.deck.co](https://status.deck.co). Subscribe to receive notifications about downtime, degraded performance, and scheduled maintenance. ## Versioning Deck uses semantic versioning (e.g., v1.6, v2.2, v3) to manage breaking changes. Each version defines how Deck interprets requests, structures responses, and delivers events. Every team has a default API version that applies to all API keys and event deliveries. New teams start on the latest stable version automatically. ## Breaking changes Deck only releases new API versions when changes would break existing integrations. Breaking changes include: * Removing or renaming fields * Changing response structures or data types * Modifying required parameters * Altering authentication or error formats * Backwards-incompatible changes to endpoint behavior * Removing endpoints Deck provides advance notice before releasing breaking changes, announced in the changelog with migration notes. ## Additive changes Deck continuously ships additive, non-breaking updates that improve the API without requiring a new version. These roll out automatically to all supported versions. Examples include: * New optional request parameters * New response fields * New endpoints or resources * New event types * New optional HTTP headers * Changes to the order of properties in responses * Changes to the length or format of object IDs (up to 255 characters) * Bug fixes that improve correctness Your integration should handle additive changes gracefully. Ignore fields you don't recognize, don't fail on unexpected enum values, and handle unfamiliar event types in your event handlers. ## Building resilient integrations * Subscribe to the [status page](https://status.deck.co) for incident notifications * Handle unknown fields and enum values without failing * Use the `request_id` on error responses when contacting support * Use [events](/events/events) instead of polling to reduce your dependency on API availability # Error handling Source: https://docs.deck.co/api/errors Every error returns a consistent structure with a deterministic type, code, and request ID. Errors are always returned as an array, regardless of how many occurred, so your error handling logic only needs one code path. ## Error structure ```json theme={null} { "errors": [ { "type": "request", "code": "input_missing", "field": "name", "message": "`name` is required and cannot be empty." } ], "request_id": "req_a1b2c3d4..." } ``` | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `type` | Error category. Determines the class of failure. | | `code` | Machine-readable error code. Use this for programmatic handling. | | `field` | The input field that caused the error, when applicable. | | `message` | Human-readable explanation. Do not rely on this for logic. | | `request_id` | Unique identifier for the request. Send this to Deck support when troubleshooting. | ## Handling errors Use `type` and `code` for branching logic. The `message` field is for logging and display only. It may change. ```javascript theme={null} if (error.type === "auth" && error.code === "auth_invalid") { promptUserToReAuthenticate(); } if (error.type === "interaction" && error.code === "interaction_timeout") { promptUserForNewMfaCode(); } if (error.type === "rate_limit") { retryWithBackoff(); } ``` ## Error types Errors are grouped as **Deck Errors**, **Request Errors**, or **Source Errors**. Only Deck Errors count against a task's [success rate](#success-rate). | Type | When it occurs | Group | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | `api` | Deck platform failure | Deck Error | | `idempotency` | Idempotency key error | Request Error | | `session` | Session could not be established | Deck Error | | `rate_limit` | Too many requests | Request Error | | `source` | External source failure or unavailability | Source Error | | `request` | Invalid input, missing fields, or unsupported action | Request Error | | `auth` | Credential or authentication method failure | Request Error | | `interaction` | MFA or interaction timeout | Request Error | | `task` | Task execution failure | Deck Error | | `storage` | File storage or extraction failure | Deck Error | | `organization` | Plan, billing, or entitlement state blocks the request. Retrying will not help; the customer must upgrade, take billing action, or wait for the next billing cycle. | Request Error | ## Success rate Success rate is the share of task runs that completed successfully over a given time period. Only Deck Errors count against it. * Runs that succeed count as successes. * Runs that fail with a Deck Error count as failures. * Runs that fail with a Request Error or Source Error are excluded from the calculation, since it was impossible for Deck to complete the task. ``` success_rate = successes / (successes + deck_error_failures) ``` ## Error codes by type ### api | Code | Description | | --------------------- | ------------------------------------------ | | `api_error` | Platform error | | `timeout` | Platform timed out | | `endpoint_deprecated` | Endpoint is deprecated and pending removal | ### idempotency | Code | Description | | ----------------------- | ---------------------------------------------- | | `idempotency_error` | Same key, different parameters | | `idempotency_key_error` | Idempotency key must be at most 256 characters | ### session | Code | Description | | --------- | ------------------------------- | | `timeout` | Session could not start in time | ### rate\_limit | Code | Description | | ------------------------ | ---------------------------- | | `rate_limit_exceeded` | Too many requests | | `session_limit_exceeded` | Agent sandbox limit exceeded | ### source | Code | Description | | ---------------------- | ----------------------------- | | `source_error` | External source failure | | `source_not_available` | Source is down or unreachable | | `source_blocked` | Source is on a blocklist | ### request These are the most common errors. They indicate a problem with your request. | Code | Description | | --------------------------- | ----------------------------------------------------------------------------- | | `input_invalid` | Validation failed | | `input_type_invalid` | Wrong type | | `input_format_invalid` | Wrong format | | `input_missing` | Required field missing | | `input_too_long` | Exceeds max length | | `input_too_short` | Below min length | | `input_duplicate` | Must be unique | | `query_param_invalid` | Unsupported query parameter | | `cursor_invalid` | Pagination cursor is invalid or expired | | `resource_not_found` | Resource does not exist | | `resource_not_ready` | Resource is not in a usable state | | `api_key_invalid` | Invalid API key | | `token_invalid` | Invalid token | | `credential_not_found` | Credential does not exist | | `source_not_found` | Source does not exist | | `action_not_supported` | Not supported for this source | | `task_not_found` | Task does not exist | | `task_not_supported` | Task not supported for this source | | `interaction_required` | Blocked until pending interaction is resolved | | `interaction_input_invalid` | Submitted interaction data is invalid | | `interaction_not_required` | No interaction was requested | | `interaction_not_found` | Interaction does not exist | | `storage_not_found` | Storage item does not exist | | `attachment_invalid` | Input file failed its security scan. Returned asynchronously on the task run. | ### auth | Code | Description | | --------------------------- | ----------------------------------------- | | `auth_method_not_supported` | Auth method not supported for this source | | `auth_not_supported` | Source does not require authentication | | `auth_required` | Authentication is required | | `auth_invalid` | Credentials are wrong | | `password_reset_required` | Source is forcing a password reset | | `account_locked` | Account is locked at the source | ### interaction | Code | Description | | --------------------- | -------------------------- | | `interaction_timeout` | Interaction window expired | ### task | Code | Description | | --------------------- | -------------------------------------------- | | `blocked` | Blocked by antibot | | `task_failed` | Task execution failed | | `task_result_unknown` | Completed but result could not be determined | | `timeout` | Execution timed out | ### storage | Code | Description | | -------------------- | -------------------------- | | `timeout` | Storage request timed out | | `extraction_failed` | Document extraction failed | | `extraction_timeout` | Extraction timed out | ### organization | Code | Description | | ----------------------- | ---------------------------------------------------- | | `quota_exceeded` | Plan quota exceeded | | `task_length_exceeded` | Task exceeded the maximum length allowed by the plan | | `feature_not_available` | Feature is not available on the current plan | | `plan_inactive` | Subscription is past due or canceled | ## Errors on resource objects When a task run fails, the `errors` array is populated on the run object itself. This is the same structure as API-level errors. ```json theme={null} { "id": "trun_a1b2c3d4...", "object": "task_run", "status": "failed", "result": "failure", "task_id": "task_a1b2c3d4...", "credential_id": "cred_a1b2c3d4...", "session_id": "sess_x9y8z7...", "agent_id": "agt_a1b2c3d4...", "source_id": "src_a1b2c3d4...", "runtime_ms": 12453, "output": null, "errors": [ { "type": "source", "code": "source_blocked", "message": "Source blocked the credential." } ], "interaction": null, "created_at": "2025-01-15T09:30:00Z", "updated_at": "2025-01-15T09:31:15Z", "request_id": "req_a1b2c3d4..." } ``` ## Request IDs Every response includes a `request_id`. When contacting Deck support, include this value to speed up troubleshooting. # External IDs Source: https://docs.deck.co/api/external-ids Map Deck credentials to users in your system with external identifiers. The `external_id` field on credentials lets you associate a Deck resource with an identifier from your own system. This is the primary mechanism for mapping Deck credentials to your users, accounts, or tenants. ## How it works When you create a credential, pass an `external_id` to link it to a record in your system: ```bash theme={null} curl -X POST "https://api.deck.co/v2/credentials" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "source_id": "src_abc", "external_id": "user_12345", "auth_method": "username_password", "auth_credentials": { "username": "...", "password": "..." } }' ``` The `external_id` is stored on the credential and returned in all API responses and event payloads for that credential. ## Constraints * Maximum length: **255 characters** * Must be a string * Can be `null` (no external mapping) * Not required to be unique across credentials, but in most cases you'll want it to be meaningful ## Multi-tenant patterns If you're building a platform where multiple end users connect to the same source, `external_id` is how you keep track of which credential belongs to which user. The simplest pattern. Each user in your system has a single credential per source. Use your internal user ID as the `external_id`. ```json theme={null} { "source_id": "src_bank", "external_id": "user_12345", "auth_method": "username_password", "auth_credentials": { ... } } ``` To find a user's credential later: ```bash theme={null} curl "https://api.deck.co/v2/credentials?source_id=src_bank&external_id=user_12345" \ -H "Authorization: Bearer sk_live_..." ``` Some users may need multiple credentials to the same source (e.g., multiple bank accounts). Use a composite identifier. ```json theme={null} { "source_id": "src_bank", "external_id": "user_12345:checking", "auth_method": "username_password", "auth_credentials": { ... } } ``` Or use your own account-level identifier: ```json theme={null} { "source_id": "src_bank", "external_id": "account_67890", "auth_method": "username_password", "auth_credentials": { ... } } ``` For B2B platforms, map credentials to organizations or teams rather than individual users. ```json theme={null} { "source_id": "src_payroll", "external_id": "org_acme_corp", "auth_method": "username_password", "auth_credentials": { ... } } ``` This lets you query all credentials for an organization: ```bash theme={null} curl "https://api.deck.co/v2/credentials?external_id=org_acme_corp" \ -H "Authorization: Bearer sk_live_..." ``` ## Updating an external ID You can update the `external_id` on an existing credential: ```bash theme={null} curl -X PATCH "https://api.deck.co/v2/credentials/{credential_id}" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "external_id": "new_user_id" }' ``` Set it to `null` to remove the mapping: ```json theme={null} { "external_id": null } ``` ## External IDs in events When a credential triggers an event (e.g., `credential.verified`), the event payload includes the credential's `external_id`. This lets you route event processing to the right user in your system without an extra API call. ```json theme={null} { "id": "evt_abc", "type": "credential.verified", "data": { "id": "cred_def", "object": "credential", "external_id": "user_12345", "status": "verified" } } ``` Use this to look up the user in your database and take action (send a notification, update a record, trigger a downstream process) without needing to call `GET /v2/credentials/{credential_id}`. ## Best practices * **Set `external_id` at creation time.** It's easier to set it upfront than to backfill later. * **Use stable identifiers.** Choose IDs that won't change, like your database primary key or a UUID. Avoid using emails or usernames that users can modify. * **Keep it consistent.** Use the same `external_id` format across all credentials for a given user. If you use `user_12345` for one source, use it for all sources. * **Use it for filtering.** The [list credentials](/api/filtering-list-endpoints) endpoint supports filtering by `external_id`, making it easy to find all credentials for a specific user. You can also search by `external_id` in the Console. # Filtering list endpoints Source: https://docs.deck.co/api/filtering-list-endpoints Narrow down API results using query parameters on list endpoints. Most list endpoints accept query parameters that let you filter results server-side. This is more efficient than fetching everything and filtering in your application. All filters are combined with AND logic. Every list endpoint also supports [cursor-based pagination](/api/pagination) via the `limit` and `cursor` parameters. ## Filters by endpoint `GET /v2/credentials` Return only credentials for a specific source. Filter by credential status. One of `unverified`, `verified`, `invalid`, `deleted`. Filter by the external ID you assigned when creating the credential. Useful for finding all credentials belonging to a specific user in your system. ```bash theme={null} curl "https://api.deck.co/v2/credentials?source_id=src_abc&status=verified" \ -H "Authorization: Bearer sk_live_..." ``` `GET /v2/tasks` Return only tasks belonging to a specific agent. Filter by task status. One of `learning`, `test`, `live`. ```bash theme={null} curl "https://api.deck.co/v2/tasks?agent_id=agt_abc&status=live" \ -H "Authorization: Bearer sk_live_..." ``` `GET /v2/task-runs` Filter by agent. Filter by credential. Returns only runs that used a specific credential. Filter by source. Filter by task. Filter by run status. One of `queued`, `running`, `cancelling`, `interaction_required`, `review_required`, `completed`, `canceled`, `failed`. Filter by run result. One of `success`, `failure`, `unknown`. Maps to the `result` field on the task run object. Return runs created after this timestamp (ISO 8601). Return runs created before this timestamp (ISO 8601). ```bash theme={null} curl "https://api.deck.co/v2/task-runs?agent_id=agt_abc&status=completed&outcome=success&created_after=2025-01-01T00:00:00Z" \ -H "Authorization: Bearer sk_live_..." ``` `GET /v2/event-destinations` Filter by destination status. One of `pending_verification`, `active`, `inactive`. ```bash theme={null} curl "https://api.deck.co/v2/event-destinations?status=active" \ -H "Authorization: Bearer sk_live_..." ``` `GET /v2/event-destinations/{destination_id}/event-deliveries` Filter by delivery status. One of `success`, `failure`. ```bash theme={null} curl "https://api.deck.co/v2/event-destinations/evtd_abc/event-deliveries?status=failure" \ -H "Authorization: Bearer sk_live_..." ``` `GET /v2/events` Filter by event type (e.g., `task_run.completed`, `credential.connected`). ```bash theme={null} curl "https://api.deck.co/v2/events?type=task_run.completed" \ -H "Authorization: Bearer sk_live_..." ``` ## Endpoints without filters The following list endpoints only support pagination (`limit` and `cursor`) with no additional filters: * `GET /v2/agents` * `GET /v2/sources` ## Combining filters with date ranges For endpoints that support `created_after` and `created_before`, you can combine them to query a specific window of time. ```bash theme={null} curl "https://api.deck.co/v2/task-runs?created_after=2025-06-01T00:00:00Z&created_before=2025-06-30T23:59:59Z&status=failed" \ -H "Authorization: Bearer sk_live_..." ``` This returns all failed task runs from June 2025. Combine date ranges with other filters to build precise queries for dashboards, reports, or debugging. ## Tips * **Filter server-side.** Fetching all records and filtering in your app wastes bandwidth and API quota. * **Combine filters.** All query parameters are AND-combined, so adding more filters narrows results. * **Use date ranges for historical queries.** `created_after` and `created_before` are the most efficient way to pull data for a time window. * **Pair with pagination.** Large result sets should use `limit` and `cursor` together with filters. See the [pagination guide](/api/pagination) for details. Navigate large result sets with cursor-based pagination. Authentication, base URLs, and request conventions. # Idempotency Source: https://docs.deck.co/api/idempotency Safely retry requests without performing the same operation twice. All `POST`, `PATCH`, and `DELETE` requests accept an `Idempotency-Key` header. Retry with the same key and Deck returns the original response instead of performing the operation again. `GET` requests are naturally idempotent and do not require a key. ## How it works Include an `Idempotency-Key` header on any `POST`, `PATCH`, or `DELETE` request. ```bash theme={null} curl -X POST https://api.deck.co/v2/agents \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \ -d '{ "name": "Hotel Reservations" }' ``` When Deck receives a request with an idempotency key and the request succeeds, it caches the response and associates it with that key. Only successful responses are cached. If the original request returned an error, retrying with the same key will execute the request again. Keys are scoped to your organization, so different organizations can use the same key independently. If the same key is sent again: * Same parameters: Deck returns the cached response. The operation is not repeated. * Different parameters: Deck returns a `409 Conflict` with an `idempotency_error` to prevent accidental misuse. * After 24 hours: the key expires and can be reused. ## When to use them Use idempotency keys on any create operation where a duplicate would cause problems: | Operation | Why | | ------------------------------- | ------------------------------------------------ | | Storing a credential | Prevents duplicate credentials for the same user | | Running a task | Prevents the same task from executing twice | | Creating agents, sources, tasks | Prevents duplicate resources | | Creating event destinations | Prevents duplicate webhook subscriptions | | Updating or deleting resources | Guarantees the operation happens exactly once | You don't need idempotency keys for `GET` requests. Reads are naturally idempotent. ## Key format Keys can be any string up to 256 characters. Keys longer than 256 characters are rejected with a `400` error. ```text theme={null} Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 ``` Common patterns: | Pattern | Example | When to use | | ------------------ | -------------------------------------- | -------------------------------------------- | | Business logic key | `user_456_run_task_789` | When the operation maps to a domain action | | Request identifier | `req_20260218_abc123` | When you track requests in your system | | UUID | `550e8400-e29b-41d4-a716-446655440000` | When you need a guaranteed-unique random key | ## Generating keys ```javascript Node.js (UUID) theme={null} const { randomUUID } = require('crypto') const idempotencyKey = randomUUID() ``` ```javascript Node.js (business key) theme={null} const idempotencyKey = `user_${userId}_run_${taskId}` ``` ```python Python (UUID) theme={null} import uuid idempotency_key = str(uuid.uuid4()) ``` ```python Python (business key) theme={null} idempotency_key = f"user_{user_id}_run_{task_id}" ``` ## Retry pattern A typical retry flow with idempotency: ```javascript theme={null} async function createAgent(name, retries = 3) { const idempotencyKey = `create_agent_${name}` for (let attempt = 1; attempt <= retries; attempt++) { try { const res = await fetch('https://api.deck.co/v2/agents', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.DECK_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey, }, body: JSON.stringify({ name }), }) return await res.json() } catch (err) { if (attempt === retries) throw err await new Promise(r => setTimeout(r, 1000 * attempt)) } } } ``` The key stays the same across retries. If the first request succeeded but the response was lost, the second request returns the cached result. If the first request failed or never reached the server, the second one executes normally since only successful responses are cached. ## Error handling **Key reuse with different parameters** returns HTTP `409`: ```json theme={null} { "errors": [ { "type": "idempotency", "code": "idempotency_error", "message": "Idempotency key has already been used with different parameters." } ], "request_id": "req_a1b2c3d4..." } ``` If you see this error, use a new key for the new request. **Key exceeds 256 characters** returns HTTP `400`: ```json theme={null} { "errors": [ { "type": "idempotency", "code": "idempotency_key_error", "message": "Idempotency key must be at most 256 characters" } ], "request_id": "req_a1b2c3d4..." } ``` # Pagination Source: https://docs.deck.co/api/pagination Page through list endpoint results using cursor-based pagination. All list endpoints in the Deck API use cursor-based pagination. Each response includes a `data` array, a `has_more` flag, and a `next_cursor` value you pass on subsequent requests to fetch the next page. ## Request parameters | Parameter | Type | Default | Description | | --------- | ------- | ------- | --------------------------------------------------------- | | `limit` | integer | 20 | Number of results per page. Min 1, max 100. | | `cursor` | string | | Cursor from a previous response. Omit for the first page. | ```bash theme={null} curl "https://api.deck.co/v2/agents?limit=50" \ -H "Authorization: Bearer sk_live_your_key_here" ``` ## Response format Every list response has the same shape: Array of resource objects for the current page. `true` if there are more results beyond this page. `false` on the last page. An opaque string to pass as the `cursor` query parameter on the next request. `null` when there are no more pages. Unique identifier for the API request. ```json theme={null} { "data": [ { "id": "agt_abc...", "object": "agent", "name": "Hotel Reservations" }, { "id": "agt_def...", "object": "agent", "name": "Expense Reports" } ], "has_more": true, "next_cursor": "eyJpZCI6ImFnZW50X2RlZiJ9", "request_id": "req_a1b2c3d4..." } ``` ## Fetching the next page Pass `next_cursor` from the previous response as the `cursor` query parameter: ```bash theme={null} curl "https://api.deck.co/v2/agents?limit=50&cursor=eyJpZCI6ImFnZW50X2RlZiJ9" \ -H "Authorization: Bearer sk_live_your_key_here" ``` Continue until `has_more` is `false`. ## Paginating through all results ```javascript Node.js theme={null} async function fetchAll(endpoint) { const results = [] let cursor = null do { const url = new URL(`https://api.deck.co/v2/${endpoint}`) url.searchParams.set('limit', '100') if (cursor) url.searchParams.set('cursor', cursor) const res = await fetch(url, { headers: { 'Authorization': `Bearer ${process.env.DECK_API_KEY}` }, }) const page = await res.json() results.push(...page.data) cursor = page.next_cursor } while (cursor) return results } const allAgents = await fetchAll('agents') ``` ```python Python theme={null} import requests def fetch_all(endpoint): results = [] cursor = None while True: params = {"limit": 100} if cursor: params["cursor"] = cursor res = requests.get( f"https://api.deck.co/v2/{endpoint}", headers={"Authorization": f"Bearer {DECK_API_KEY}"}, params=params, ) page = res.json() results.extend(page["data"]) if not page["has_more"]: break cursor = page["next_cursor"] return results all_agents = fetch_all("agents") ``` ```go Go theme={null} func fetchAll(endpoint string) ([]json.RawMessage, error) { var results []json.RawMessage cursor := "" for { u := fmt.Sprintf("https://api.deck.co/v2/%s?limit=100", endpoint) if cursor != "" { u += "&cursor=" + cursor } req, _ := http.NewRequest("GET", u, nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("DECK_API_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var page struct { Data []json.RawMessage `json:"data"` HasMore bool `json:"has_more"` NextCursor *string `json:"next_cursor"` } json.NewDecoder(resp.Body).Decode(&page) results = append(results, page.Data...) if !page.HasMore || page.NextCursor == nil { break } cursor = *page.NextCursor } return results, nil } ``` ## Filtering and sorting List endpoints may accept additional query parameters for filtering. Filters are applied server-side before pagination, so the `limit` applies to filtered results. ```bash theme={null} curl "https://api.deck.co/v2/task-runs?credential_id=cred_abc123&limit=50" \ -H "Authorization: Bearer sk_live_your_key_here" ``` Results are returned in reverse chronological order (newest first) by default. ## Cursors are opaque Cursor values are opaque strings. Do not parse, construct, or store them long-term. They encode internal position state and may change format between API versions. * Pass them exactly as received from the API. * Do not reuse cursors across different endpoints or filter combinations. * If a cursor becomes invalid, the API returns a `400` error with `cursor_invalid`. Start from the first page. ## Stable ordering Deck guarantees stable ordering within a paginated sequence. If no records are created or deleted between requests, every record appears exactly once across all pages. Records created after you begin paginating may or may not appear depending on their position in the sort order. # Rate limits & concurrency Source: https://docs.deck.co/api/rate-limits How Deck rate limits API requests and manages session concurrency. Deck enforces two types of limits: rate limits on API requests and concurrency limits on sessions running in agent sandboxes. ## Limits The rate limit is 200 requests per 10 seconds per organization. Limits are applied across all API keys in your organization. If you have multiple services making requests with different keys, they share the same quota. ## Rate limited responses When a request is rate limited, Deck returns: ```json theme={null} { "errors": [ { "type": "rate_limit", "code": "rate_limit_exceeded", "message": "Too many requests." } ], "request_id": "req_a1b2c3d4..." } ``` ## Handling rate limits Implement retries with exponential backoff when you receive a `429` response. ```javascript theme={null} async function requestWithBackoff(url, options, retries = 5) { for (let attempt = 0; attempt < retries; attempt++) { const res = await fetch(url, options) if (res.status !== 429) return res const delay = Math.min(1000 * Math.pow(2, attempt), 30000) await new Promise(r => setTimeout(r, delay)) } throw new Error('Rate limit retries exhausted') } ``` ## Session concurrency Each task that Deck runs executes in an agent sandbox tied to a session. The number of sessions your organization can run concurrently depends on your [plan](https://deck.co/pricing). This limit applies across all sessions in your organization regardless of connector or credential. When you submit a task that would exceed your concurrency limit, Deck returns a `429` status code: ```json theme={null} { "errors": [ { "type": "rate_limit", "code": "session_limit_exceeded", "message": "Agent sandbox limit exceeded." } ], "request_id": "req_a1b2c3d4..." } ``` Retry with backoff until a running session reaches a terminal state and a slot becomes available. ## Best practices * Use events instead of polling. Register an [event destination](/events/events) and let Deck push status updates to you rather than polling `GET` endpoints in a loop. * Paginate list requests. Use `limit` and `cursor` to fetch results in pages rather than requesting large datasets in a single call. * Stagger task submissions. If you have many tasks to run, queue them and submit the next one after the previous completes. Use [events](/events/events) to know when a session finishes rather than polling. # Resource IDs Source: https://docs.deck.co/api/resource-ids How Deck identifies objects across the API. Every object in the Deck API has a unique identifier with a type prefix. The prefix tells you what kind of resource the ID refers to, making it easier to debug, log, and route objects through your system without additional lookups. ## Format All IDs follow the same pattern: ```text theme={null} {type_prefix}_{unique_string} ``` The prefix is always lowercase, followed by an underscore, followed by a URL-safe alphanumeric string. IDs are case-sensitive. ## ID Reference | Prefix | Resource | Example | Description | | ---------- | ----------------- | ------------------- | ------------------------------------------------------------------------ | | `agt_` | Agent | `agt_a1b2c3d4` | An automation agent scoped to a use case. | | `src_` | Source | `src_a1b2c3d4` | A website or service your agent connects to. | | `cred_` | Credential | `cred_a1b2c3d4` | Stored authentication details for a user on a source. | | `sess_` | Session | `sess_a1b2c3d4` | An isolated compute session used to execute tasks. | | `task_` | Task | `task_abc123` | A defined action an agent can perform. | | `trun_` | Task Run | `trun_a1b2c3d4` | A single execution of a task. | | `stor_` | Storage Item | `stor_a1b2c3d4` | A file or document captured during a task run. | | `evt_` | Event | `evt_a1b2c3d4` | A lifecycle event emitted by the platform. | | `evtd_` | Event Destination | `evtd_a1b2c3d4` | An endpoint that receives events. | | `edlv_` | Event Delivery | `edlv_a1b2c3d4` | A record of an event sent to a destination. | | `req_` | Request | `req_a1b2c3d4` | A unique identifier for an API request, returned in error responses. | | `sk_live_` | API Key | `sk_live_abc123...` | Your API key. Not a resource ID, but follows the same prefix convention. | ## Using prefixed IDs ### Type checking Because the prefix encodes the resource type, you can validate IDs before making API calls. ```javascript theme={null} function getResourceType(id) { const prefix = id.split("_").slice(0, -1).join("_"); const types = { agt: "agent", src: "source", cred: "credential", sess: "session", task: "task", trun: "task_run", stor: "storage", evt: "event", evtd: "event_destination", edlv: "event_delivery", }; return types[prefix] || null; } ``` ### Logging and debugging Prefixed IDs make it easy to trace activity across resources in your logs. A log entry containing `cred_a1b2c3d4` and `trun_x9y8z7` immediately tells you which credential and task run were involved, without needing to cross-reference separate tables. ### Cross-referencing resources Many API responses include related resource IDs. A task run object, for example, includes `task_id`, `credential_id`, `agent_id`, and `source_id`. The prefixes let you quickly verify the relationships are correct. ```json theme={null} { "id": "trun_a1b2c3d4", "object": "task_run", "task_id": "task_abc123", "credential_id": "cred_a1b2c3d4", "session_id": "sess_a1b2c3d4", "agent_id": "agt_a1b2c3d4", "source_id": "src_a1b2c3d4", "status": "completed" } ``` ## Notes * IDs are immutable. Once assigned, they never change. * IDs are globally unique. You will never see the same ID across different resource types or organizations. * IDs are case-sensitive. `agt_A1B2` and `agt_a1b2` are different identifiers. * Passing an ID with the wrong prefix to an endpoint returns a `400` validation error. # Using the API Source: https://docs.deck.co/api/using-the-api Base URL, authentication, request format, and response conventions. The Deck API is a REST API that accepts JSON request bodies and returns JSON responses. All endpoints require authentication via a Bearer token. ## Base URL All requests are made against the Deck API at: ```text theme={null} https://api.deck.co/v2 ``` ## Authentication Every request requires an API key passed as a Bearer token in the `Authorization` header. Keys are available in your [Console](https://console.deck.co). ```bash theme={null} curl https://api.deck.co/v2/test \ -H "Authorization: Bearer sk_live_your_key_here" ``` ## Verify your key Call the test endpoint to confirm your key is valid. ```bash theme={null} curl https://api.deck.co/v2/test \ -H "Authorization: Bearer sk_live_your_key_here" ``` ```json theme={null} { "status": "ready", "environment": "live", "request_id": "req_a1b2c3d4..." } ``` ## Request format All requests use `Content-Type: application/json`. Pass request bodies as JSON. ```bash theme={null} curl -X POST https://api.deck.co/v2/agents \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "Hotel Reservations", "description": "Fetch and manage hotel bookings" }' ``` Path parameters are part of the URL. Query parameters are used for filtering and pagination on list endpoints. ## Response format Every resource response includes a standard set of fields: ```json theme={null} { "id": "agt_a1b2c3d4...", "object": "agent", "name": "Hotel Reservations", "description": "Fetch and manage hotel bookings", "created_at": "2025-01-15T09:00:00Z", "updated_at": "2025-01-15T09:00:00Z", "request_id": "req_a1b2c3d4..." } ``` | Field | Description | | ------------ | ------------------------------------------------------------------------------------------------------- | | `id` | Unique identifier, prefixed by resource type (`agt_`, `src_`, `cred_`, `sess_`, `task_`, `trun_`, etc.) | | `object` | Resource type as a string. Useful for parsing polymorphic responses. | | `created_at` | ISO 8601 timestamp | | `updated_at` | ISO 8601 timestamp | | `request_id` | Unique identifier for the API request. Include this when contacting support. | Delete responses return a confirmation object: ```json theme={null} { "id": "agt_a1b2c3d4...", "object": "agent", "deleted": true, "request_id": "req_a1b2c3d4..." } ``` ## Request IDs Every response includes a `request_id`. When something goes wrong, include this value in support requests so Deck can trace exactly what happened. ## Next steps Cursor-based pagination for list endpoints. Safely retry requests without creating duplicates. Error structure, types, and codes. Request limits and retry strategies. # Auth Component Source: https://docs.deck.co/components/auth A drop-in React component for secure credential capture, source selection, and authentication. The Auth Component is a pre-built React component that collects end-user credentials, stores them in the [Credential Vault](/platform/credential-management), and creates a [Credential](/concepts/credentials). Credentials never touch your systems. It handles source selection, username/password capture, [interactions](/guides/interactions) (MFA, security questions), and error states out of the box. Want full customization and control over the UX? See [Building your auth flow](/guides/building-auth-ui) to build your own using the API directly. ## How it works ```mermaid theme={null} sequenceDiagram participant Backend as Your Server participant Frontend as Your Frontend participant Component as Auth Component participant API as Deck API participant Vault as Credential Vault Backend->>API: POST /v2/token API-->>Backend: session token Backend-->>Frontend: pass token Frontend->>Component: Mount Component->>Component: User selects source & enters credentials Component->>API: Credentials (encrypted) API->>Vault: Encrypt & store API-->>Component: Credential created Component-->>Frontend: onSuccess({ credentialId }) ``` 1. **Your server** creates a session token via `POST /v2/token` 2. **Your frontend** mounts `` with the token and all configuration 3. **The component** renders a secure UI (hosted at `auth.components.deck.co`) where the user selects a source and enters credentials 4. **Credentials** go from the component directly to the Deck API and into the Credential Vault. They never touch your page 5. **On success**, the component returns the new Credential ID via `onSuccess` ## Restrictions * Only supports `auth_method: username_password` * Does not support chaining to tasks with inputs * Does not support `source_fields` on the credential ## Installation ```bash theme={null} npm install @deckco/auth ``` ```tsx theme={null} import { DeckAuthComponent } from '@deckco/auth'; ``` React 18+ is a peer dependency. ## Quick example Create a session token on your server: ```typescript Node theme={null} const response = await fetch('https://api.deck.co/v2/token', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.DECK_SECRET_KEY}`, }, }); const { token } = await response.json(); ``` ```bash curl theme={null} curl -X POST https://api.deck.co/v2/token \ -H "Authorization: Bearer $DECK_SECRET_KEY" ``` ```python Python theme={null} import os, requests response = requests.post( "https://api.deck.co/v2/token", headers={"Authorization": f"Bearer {os.environ['DECK_SECRET_KEY']}"}, ) token = response.json()["token"] ``` Mount the component in your React app: ```tsx Client theme={null} { console.log('Credential created:', credentialId); }} onError={({ type, code, message }) => { console.error('Auth failed:', code, message); }} onClose={() => { // User dismissed the component. Unmount, navigate, etc. }} /> ``` ## Session token Every auth component session starts with a server-side token. The token is authentication only. It proves the request came from your backend. All configuration (sourceId, appearance, etc.) is passed on the component, not the token. ### `POST /v2/token` Creates a session token for the Auth Component. **Authentication:** `Authorization: Bearer sk_live_...` **Request body:** None **Response (201):** ```json theme={null} { "id": "tok_abc123...", "object": "token", "token": "tk_abc123...", "expires_at": "2026-04-10T12:30:00Z", "created_at": "2026-04-10T12:00:00Z", "request_id": "req_xyz789" } ``` | Field | Type | Description | | ------------ | -------- | -------------------------------------------------------- | | `id` | string | Token identifier, prefixed `tok_` | | `object` | string | Always `token` | | `token` | string | The token value to pass to the component, prefixed `tk_` | | `expires_at` | ISO 8601 | 30 minutes from creation | | `created_at` | ISO 8601 | Creation timestamp | | `request_id` | string | Request ID for support | ### Token lifecycle * Tokens expire **30 minutes** after creation * Expired tokens return a clear error to `onError` ## Component props | Prop | Type | Description | | -------------- | -------------------- | ------------------------------------------------------------------------------------------------------- | | `token` | `string` | **Required.** Session token from `POST /v2/token`. | | `sourceId` | `string \| string[]` | Pre-select a single source (skips picker) or filter to multiple. | | `credentialId` | `string` | If provided, enters [Update Mode](#update-mode). Re-collects credentials for an existing credential. | | `externalId` | `string` | Your user ID. Passed back in `onSuccess` and persisted on the credential. | | `taskId` | `string` | If provided, fires a verification [task run](#task-linking) after credential creation/update. | | `appearance` | `AppearanceConfig` | Full theming control. See [Appearance](#appearance). | | `language` | `"en-US" \| "fr-CA"` | Language for all component-rendered copy. Defaults to `"en-US"`. See [Language](#language). | | `taskRunView` | `"status" \| "live"` | How the task run is displayed while executing. Defaults to `"status"`. | | `onSuccess` | `(data) => void` | **Required.** Called when credential creation/update succeeds. | | `onError` | `(data) => void` | Called on failure. See [Error handling](#error-handling). | | `onClose` | `() => void` | If provided, shows a close button. Called when user clicks it. If omitted, no close button is rendered. | The component does not own its lifecycle. Your app does. When `onClose` fires, you decide what happens (unmount, navigate, show a confirmation, etc.). ## Callbacks ### `onSuccess` ```typescript theme={null} onSuccess: (data: { credentialId: string; externalId: string | null; taskRunId: string | null; verified: boolean | null; }) => void; ``` | Field | Type | Description | | -------------- | ----------------- | -------------------------------------------------------------------------------- | | `credentialId` | `string` | The created or updated credential | | `externalId` | `string \| null` | Your external ID, if provided | | `taskRunId` | `string \| null` | Verification task run ID. `null` if no `taskId` was passed | | `verified` | `boolean \| null` | `true` if run succeeded, `false` if run failed, `null` if no `taskId` was passed | ### `onError` ```typescript theme={null} onError: (data: { type: string; code: string; message: string; }) => void; ``` `type` is the error category. `code` is the machine-readable identifier. `message` is human-readable and may change. Switch on `type` and `code`, not `message`. See [Error handling](#error-handling) for all error codes. ### `onClose` ```typescript theme={null} onClose: () => void; ``` If `onClose` is provided, the component renders a close button. When clicked, `onClose` fires and you handle the teardown. If `onClose` is omitted, no close button is rendered. Pressing Escape also fires `onClose` if provided. If a task run is in progress when the user closes the component, the task run continues running in the background. To cancel it, use the [cancel endpoint](/api-reference/task-runs/cancel-a-task-run) from your `onClose` handler. ## Source selection The `sourceId` prop controls what the user sees before credential capture. | `sourceId` | Behavior | | -------------------------------- | ------------------------------------------------------------------------------ | | Not provided | Full source search. User searches across all sources in your organization. | | Single ID (`"src_abc123"`) | Skips source selection. Goes straight to credential capture. | | Array (`["src_abc", "src_def"]`) | Filtered selection. User picks from the provided sources in your organization. | ## Update mode Pass a `credentialId` to re-collect credentials for an existing credential instead of creating a new one. | `credentialId` | `sourceId` | Behavior | | -------------- | ---------- | -------------------------------------------------------------- | | absent | absent | Create mode. Full source search. | | absent | present | Create mode. Skip/filter source selection. | | present | ignored | Update mode. Skip source selection, patch existing credential. | ```tsx theme={null} { console.log('Credentials updated for:', credentialId); }} /> ``` ## Task linking Pass a `taskId` to verify credentials immediately after creation. The component fires a [task run](/concepts/task-runs) using the new credential, and the task run view stays visible while it executes. | `taskId` | Auth outcome | Run outcome | Credential status | `onSuccess` payload | | -------- | ------------ | ------------ | ----------------- | --------------------------------------------------------------- | | absent | n/a | n/a | `unverified` | `{ credentialId, externalId, taskRunId: null, verified: null }` | | present | succeeds | run succeeds | `verified` | `{ credentialId, externalId, taskRunId, verified: true }` | | present | succeeds | run fails | `verified` | `{ credentialId, externalId, taskRunId, verified: false }` | | present | fails | run fails | `invalid` | `{ credentialId, externalId, taskRunId, verified: false }` | Credential status reflects whether the agent could authenticate, not whether the run finished successfully. As long as auth succeeds, the credential becomes `verified` even if the run later fails. A failed run does **not** delete or invalidate the credential. If auth itself fails, the credential becomes `invalid`. The task you pass as `taskId` must actually authenticate with the source. A task that only visits a public page will not challenge the credential, and running it will not move the credential from `unverified` to `verified`. Pick (or create) a task whose first step is logging in. ### `taskRunView` With `taskId` present, the task run view is shown while the run executes: * `"status"`: spinner with status text (e.g., "Connecting...", "Verifying credentials...") * `"live"`: live view of the task run Without `taskId`, no task run view is shown. ## Appearance Customize the component's look via the `appearance` prop. ```tsx theme={null} ``` ### Theme | Value | Description | | ----------- | -------------------------------- | | `"default"` | Light theme with neutral styling | | `"dark"` | Dark theme | ### Variables | Variable | Description | | ------------------------ | --------------------------------------- | | `colorPrimary` | Primary button and accent color | | `colorBackground` | Card background | | `colorForeground` | Main text color | | `colorPrimaryForeground` | Text on primary buttons | | `colorNeutral` | Neutral accent | | `colorDanger` | Error states | | `colorSuccess` | Success states | | `colorMuted` | Muted backgrounds (hover, etc.) | | `colorMutedForeground` | Secondary text | | `colorBorder` | Border color | | `colorInput` | Input border color | | `colorRing` | Focus ring color | | `fontFamily` | Font stack | | `borderRadius` | Border radius in pixels (e.g., `8`) | | `shadow` | Shadow token for card/elevated surfaces | ### Branding | Variable | Description | | --------------- | ----------------------------- | | `logoUrl` | Logo shown in the card header | | `logoPlacement` | `"top"` or `"left"` | Upload a logo in the Deck console under **Settings → Branding**. Deck hosts it and returns a CDN URL on `content.cdn.deck.co`. Pass that as `logoUrl`. Accepted formats are PNG, JPG, JPEG, GIF, and WebP (no SVG). The logo renders into a 40×40 box and is hidden if it fails to load. For security, the component only loads images from Deck's content CDN, `data:` URIs, and `cdn.brandfetch.io`. Arbitrary external URLs (for example, `https://your-company.com/logo.png`) are blocked by its content security policy and will not render. ## Language Set the language for all component-rendered copy via the `language` prop. ```tsx theme={null} ``` | Value | Description | | --------- | -------------------- | | `"en-US"` | US English. Default. | | `"fr-CA"` | Canadian French. | `language` applies to copy the component owns: source picker, credential capture labels and helper text, interaction prompts (generic labels and submit/continue buttons), in-UI error messages, the task run status view (`taskRunView: "status"`), and the close button and accessibility labels. Content returned by the source itself (source names, interaction field labels, and other source-defined strings) is not translated by the component and is rendered as the source returns it. ## Interactions Sources may require additional input mid-flow: an MFA code, a security question, an account selection. The component handles these automatically as **interactions**: a generic pause-and-prompt primitive. When an interaction is required: 1. The component transitions to an interaction step and renders the fields from the source's response 2. The user fills in the fields and submits 3. The source validates. On accept, the flow continues 4. Some sources require multiple sequential interactions (e.g., MFA code then a security question) The component does not enforce its own interaction timeout. Timeout windows are set by the source and handled by the backend. The user cannot go back to credential entry from an interaction step (credentials have already been created). See [Interactions](/guides/interactions) for more on how Deck models interactions. ## Error handling All errors surface through `onError` with a uniform `{ type, code, message }` shape. ### Token errors | Type | Code | Description | | --------- | --------------- | ------------------------ | | `request` | `token_invalid` | Invalid or expired token | ### Credential errors | Type | Code | Description | | --------- | --------------------------- | ------------------------------------------------- | | `auth` | `auth_invalid` | Credentials rejected by the source | | `auth` | `auth_method_not_supported` | Source does not support the attempted auth method | | `auth` | `password_reset_required` | Source is forcing a password reset | | `auth` | `account_locked` | Account is locked at the source | | `request` | `credential_not_found` | Update mode: credentialId does not exist | ### Source errors | Type | Code | Description | | --------- | ---------------------- | ------------------------------------- | | `request` | `source_not_found` | The sourceId does not exist | | `source` | `source_not_available` | Source is temporarily unreachable | | `source` | `source_error` | Source returned an unexpected failure | ### Interaction errors | Type | Code | Description | | ------------- | --------------------------- | ------------------------------------------------------ | | `interaction` | `interaction_timeout` | Interaction window expired (source-defined) | | `request` | `interaction_input_invalid` | Submitted input didn't match the declared field schema | ### Error behavior * **Token errors** (`token_invalid`) terminate the session. Create a new token and remount. * **Credential errors** (`auth_invalid`, `account_locked`, etc.) terminate the session. * **Interaction errors** (`interaction_timeout`) terminate the session. Wrong user input is not an error, the source emits another prompt and the user retries in-place. ## Embed by URL For clients that can't run the React SDK, load the component's URL in a `WebView` (for example, in a React Native app) or an `