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

# Callbacks

> Get a signed, retried POST when an operation you started finishes, instead of polling.

> [!WARNING]
>
> **Proposed — not available yet.** This page describes a design under review. Today's `callback_url` on questions is documented in [Webhooks](/webhooks).

Pass a `callback_url` when you start long-running work, and Wordsmith sends it a signed `POST` when the work finishes. Nothing has to poll. Every endpoint that starts an [operation](/proposed/long-running-operations) accepts it.

## What changes from today

| | Today | Proposed |
| - | - | - |
| Where | Questions only | Every endpoint that starts an operation |
| When | On success only | On success, failure and cancellation |
| Delivery | Once, no retry | At least once, retried for 24 hours |
| Signature | `Wordsmith-Signature` | Unchanged, so existing verifiers keep working |

Callbacks are for API-key callers, as today. An OAuth token has no webhook secret to sign with, so an OAuth caller waits with [long-polling](/proposed/long-running-operations#three-ways-to-wait) instead.

## Use a callback

```bash theme={null}
curl -X POST https://api.wordsmith.ai/api/v1/reviews \
  -H "Authorization: Bearer sk-ws-api1-your_api_key_here" \
  -H "Idempotency-Key: 6f1d2c3e-review-nda-42" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "playbook",
    "file_ids": ["9a1d7e44-2b6c-4f08-8d3a-71c5e0b9f2a6"],
    "playbook_id": "2c4b8f90-5d1e-4a37-b6e2-c8f0a3d71e45",
    "callback_url": "https://your-app.com/webhooks/wordsmith"
  }'
```

`callback_url` must be a public `https` URL. Private, loopback and link-local addresses are refused with `400 invalid_callback_url`. The API key must have a webhook secret, because every callback is signed: a key without one is refused with `400 callback_signing_unavailable`, before anything starts.

## Payload

The body is an event wrapping the finished [operation](/proposed/objects#operation):

```json theme={null}
{
  "id": "4c2e9a71-5b3f-4d08-9e6a-1f7c0b2d8e35",
  "type": "operation.succeeded",
  "created_at": "2026-10-13T09:03:41Z",
  "data": {
    "id": "3b8e0f1c-6a2d-4e57-9c1b-2f7d8a4e6b90",
    "type": "review",
    "status": "succeeded",
    "resource": { "type": "review", "id": "5f0c9a12-8e3b-4d71-a6c4-0b2e9f7d3c58" },
    "result": {
      "review_id": "5f0c9a12-8e3b-4d71-a6c4-0b2e9f7d3c58",
      "findings": { "total": 32, "issues": 7, "high": 2, "medium": 3, "low": 2 },
      "redline_file_id": "c71e2a90-4b3d-4f68-9e15-8d0a6b2f3c47"
    }
  }
}
```

`type` is `operation.succeeded`, `operation.failed` or `operation.cancelled`.

Questions keep [today's payload](/webhooks#webhook-payload-format), so existing integrations need no change. They now also get a callback when a question fails, with `status: "error"`.

## Verify the signature

Each callback carries a `Wordsmith-Signature: t=…,v1=…` header, signed with the API key's webhook secret. Verify it as described in [Validating webhook signatures](/webhooks#validating-webhook-signatures). A retry is signed again with a fresh timestamp, so the 60-second freshness check also works on retries.

Wordsmith never sends an unsigned callback. A `callback_url` from an API key without a webhook secret is refused when the work starts. If the API key that started the operation is deleted before it finishes, the callback is not sent and not retried. Read the outcome with [Get Operation](/api-reference/operations/get-operation).

## Delivery

* **At least once.** The same callback can arrive twice. Drop duplicates by the event `id`, or by the `Wordsmith-Event-Id` header on question callbacks.
* **One outcome per operation.** Each operation sends one terminal callback. A retry with the same `Idempotency-Key` after a retryable `request_failed` starts a new operation, which sends its own callback under its own operation id.
* **Respond fast.** Return any `2xx` within 10 seconds, then do the work.
* **Retries.** A non-`2xx` response or a timeout is retried with exponential back-off for 24 hours. After that Wordsmith gives up.
* **The API is the source of truth.** If you missed a callback, read the operation with [Get Operation](/api-reference/operations/get-operation), or list recent ones with [List Operations](/api-reference/operations/list-operations).


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