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

# Callback Payload

> The webhook Manka delivers when an asynchronous analysis finishes.

If a `callback_url` was supplied when submitting via [`POST /analysis`](/api-reference/analysis/submit) or [`POST /analysis-combined`](/api-reference/analysis-combined), Manka will `POST` the result to that URL when the job completes or fails.

<Warning>
  Manka makes **one delivery attempt with no retries**. Your endpoint must respond with any `2xx` status to acknowledge receipt.
</Warning>

## Payload shape

### Success

<ResponseField name="status" type="string" required>
  Always `"completed"` on success.
</ResponseField>

<ResponseField name="task_id" type="string" required>
  Matches the `task_id` returned on submission.
</ResponseField>

<ResponseField name="result" type="object" required>
  The full analysis output.
</ResponseField>

```json theme={null}
{
  "status": "completed",
  "task_id": "3f7a1c2e-8b4d-4e9f-a1b2-c3d4e5f60718",
  "result": { "...": "..." }
}
```

### Failure

<ResponseField name="status" type="string" required>
  Always `"failed"` on failure.
</ResponseField>

<ResponseField name="task_id" type="string" required>
  Matches the `task_id` returned on submission.
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable reason the analysis could not be completed.
</ResponseField>

```json theme={null}
{
  "status": "failed",
  "task_id": "3f7a1c2e-8b4d-4e9f-a1b2-c3d4e5f60718",
  "error": "Unable to process this PDF"
}
```

## Receiver recommendations

* **Idempotency** — key downstream actions on `task_id` so a duplicate delivery is harmless.
* **Respond fast** — acknowledge with `2xx` and process out-of-band; avoid blocking on long downstream work.
* **Fall back to polling** — because there are no retries, treat the callback as best-effort and pair it with [`GET /analysis/:task_id`](/api-reference/analysis/poll) if delivery is missed.

### Minimal receiver

<CodeGroup>
  ```python Flask theme={null}
  from flask import Flask, request

  app = Flask(__name__)

  @app.post("/manka/webhook")
  def manka_webhook():
      payload = request.get_json()
      task_id = payload["task_id"]
      if payload["status"] == "completed":
          save_result(task_id, payload["result"])
      else:
          mark_failed(task_id, payload["error"])
      return "", 204
  ```

  ```javascript Express theme={null}
  import express from "express";

  const app = express();
  app.use(express.json());

  app.post("/manka/webhook", (req, res) => {
    const { status, task_id, result, error } = req.body;
    if (status === "completed") saveResult(task_id, result);
    else markFailed(task_id, error);
    res.sendStatus(204);
  });
  ```
</CodeGroup>
