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

# Webhook management API

> Configure task notifications through Dashboard, API or MCP, verify signatures, and troubleshoot delivery.

Webhooks notify your app when an asynchronous task completes, fails or times out. Configure them in [Dashboard → API → Webhooks](https://tokenlab.sh/dashboard/api?tab=webhooks), through the Management API, or through TokenLab MCP's `full` profile. They share the same workspace endpoints and delivery history.

## Which credential to use

| Credential               | Purpose                                                | Where it is used                                           |
| ------------------------ | ------------------------------------------------------ | ---------------------------------------------------------- |
| Management Token `mt-…`  | Create, list, update, delete, test and rotate webhooks | `Authorization: Bearer mt-…` on `/v1/management/webhooks*` |
| API key `sk-…`           | Submit model requests and query task status            | Model APIs and `/v1/tasks/{id}`                            |
| Signing secret `whsec_…` | Verify that a received notification is authentic       | Your receiver; never use it as an API Bearer token         |

Create a Management Token at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens). Tokens are workspace scoped: choose the same workspace as the API key submitting tasks. A token cannot read or modify another workspace's endpoints. API keys and signing secrets are rejected by the Management API. Dashboard configuration uses your signed-in workspace admin session.

Store `mt-…` and `whsec_…` on your backend. Do not embed them in browser code, prompts, repository files or URLs. Management Tokens also authorize other workspace management operations; they are not webhook-only credentials.

## Configure an endpoint

```bash theme={null}
export TOKENLAB_MANAGEMENT_TOKEN="mt-your-management-token"
curl https://api.tokenlab.sh/v1/management/webhooks \
  -H "Authorization: Bearer $TOKENLAB_MANAGEMENT_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://your-app.example/webhooks/tokenlab","events":["task.completed","task.failed","task.timeout"],"description":"Production task results"}'
```

The `201` response includes `id` and a one-time `secret` beginning with `whsec_`. Store the secret before leaving this step. List/get/update responses never expose it. Up to 10 endpoints can be configured per workspace. URLs must use public HTTPS and cannot contain credentials, query strings or fragments. Redirects are not followed.

| Operation                                                                                                                                              | Endpoint                                                             |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| [List](/api-reference/management/list-webhooks) / [Create](/api-reference/management/create-webhook)                                                   | `GET` / `POST /v1/management/webhooks`                               |
| [Get](/api-reference/management/get-webhook) / [Update](/api-reference/management/update-webhook) / [Delete](/api-reference/management/delete-webhook) | `GET` / `PATCH` / `DELETE /v1/management/webhooks/{webhookId}`       |
| [Rotate signing secret](/api-reference/management/rotate-webhook-secret)                                                                               | `POST /v1/management/webhooks/{webhookId}/rotate-secret`             |
| [Send test](/api-reference/management/test-webhook)                                                                                                    | `POST /v1/management/webhooks/{webhookId}/test`                      |
| [Delivery history](/api-reference/management/list-webhook-deliveries)                                                                                  | `GET /v1/management/webhooks/{webhookId}/deliveries?page=1&limit=50` |

Pause with `PATCH {"is_active":false}`; resume with `PATCH {"is_active":true}`. Resuming resets the consecutive failure count. Rotation returns a new secret once; configure it on your receiver immediately. Requests already in flight may still carry the previous signature, so briefly accept both secrets during rotation. Rotation is not reversible.

## Events and payloads

Subscriptions cover future terminal async task events; synchronous results and historical tasks are not replayed. Receive all workspace tasks for the selected event types and match `data.taskId` to the ID returned when you created the task. Callback fields use camelCase; management response fields use snake\_case.

```json theme={null}
{
  "id": "event-unique-id",
  "type": "task.completed",
  "created": 1790000000,
  "data": {
    "taskType": "video",
    "taskId": "ldtask_0123456789abcdef0123456789abcdef",
    "model": "your-selected-model",
    "durationMs": 42000,
    "resultUrls": ["https://your-result-url.example/video.mp4"],
    "settledCost": 0.12
  }
}
```

| Event            | Data                                                                              |
| ---------------- | --------------------------------------------------------------------------------- |
| `task.completed` | `taskType`, `taskId`, optional `model`, `durationMs`, `resultUrls`, `settledCost` |
| `task.failed`    | `taskType`, `taskId`, `error`, `errorCode`, `retryable`, `refundOutcome`          |
| `task.timeout`   | `taskType`, `taskId`, `durationMs`, `refundOutcome`                               |
| `webhook.test`   | Test message and configured subscriptions; sent only by the test operation        |

Fields depend on the task and can be absent. Use `GET /v1/tasks/{id}` with the original workspace's `sk-…` key for the authoritative result and billing status. An event's `retryable` describes the generation failure; it is not an instruction to poll a terminal task indefinitely or automatically submit a new billable task.

## Verify before processing

Each POST includes `X-Webhook-ID`, `X-Webhook-Timestamp` (Unix seconds) and `X-Webhook-Signature` (`sha256=<hex>`). Compute HMAC-SHA256 over the exact timestamp string, a period, and the **raw request body bytes**, using the complete `whsec_…` secret. Do not parse and reserialize JSON before verification.

```js theme={null}
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, headers, secrets) {
  const timestamp = headers['x-webhook-timestamp'];
  const signature = headers['x-webhook-signature'];
  if (typeof timestamp !== 'string' || !/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  if (typeof signature !== 'string' || !/^sha256=[a-f0-9]{64}$/.test(signature)) return false;
  const received = Buffer.from(signature.slice(7), 'hex');
  return secrets.some(secret => {
    const expected = createHmac('sha256', secret).update(timestamp + '.').update(rawBody).digest();
    return timingSafeEqual(expected, received);
  });
}
```

After verification, check the body `id` matches `X-Webhook-ID`, persist the event ID with your work item atomically, and return `2xx` quickly. Process heavy work from your own queue. Deduplicate by event ID within the receiving endpoint: deliveries can repeat and ordering is not guaranteed. A five-minute timestamp tolerance limits replay age; ID deduplication prevents repeated processing within that window.

## Retries and recovery

Each delivery cycle makes up to three attempts, waiting 1 second then 4 seconds. Each HTTP attempt has a 10-second timeout and a fresh timestamp/signature. Network failures, `429`, and `5xx` are retryable. Other `4xx`, redirects and invalid network targets stop that cycle without retrying. Transient failures can trigger later event retries with the same delivery ID. Ten consecutive failed cycles automatically pause the endpoint.

Use the test operation and delivery history to see `outcome`, `http_status`, `attempts` and `delivered_at`. A successful test API response (`200`) only means the attempt was recorded: check `outcome == "delivered"`. Fix the receiver, resume the endpoint, then send another test. History contains delivery metadata, not payloads; manual replay of old events is not available. Keep task IDs and reconcile with task status if your receiver was paused or unavailable.

## MCP configuration

Webhook management tools are available in the `full` profile. The management credential is separate from the inference key; catalog and webhook management can work without an inference key.

```json theme={null}
{
  "mcpServers": {
    "tokenlab": {
      "command": "npx",
      "args": ["-y", "@tokenlabai/mcp-server@latest"],
      "env": {
        "TOKENLAB_MCP_TOOL_PROFILE": "full",
        "TOKENLAB_MANAGEMENT_TOKEN": "mt-your-management-token",
        "TOKENLAB_API_KEY": "sk-your-inference-key"
      }
    }
  }
}
```

Tools: `list_webhooks`, `create_webhook`, `get_webhook`, `update_webhook`, `delete_webhook`, `rotate_webhook_secret`, `test_webhook`, `list_webhook_deliveries`. Never paste credentials into a tool argument. Grant management access only to trusted MCP clients.

## Polling fallback

Webhooks do not remove status-query access or impose a new polling limit. If you poll, use the creation response's `poll_url`, back off while pending, and stop on terminal states. Stop on `401`, `403`, `404`, or `error.retryable == false`; retry transient `503 async_task_owner_unavailable` with backoff. A missing or expired task returns `404 async_task_not_found`. Repeatedly querying a missing task cannot recreate it.

The [Seedance compatibility API's `callback_url`](/api-reference/video/create-volc-compatible-seedance-task) is a separate per-request callback contract. It can be set when creating that task; it does not use these workspace event payloads or HMAC headers.
