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

# Use Webhooks

> Deliver task results to your backend with dashboard-managed or request-level webhooks.

Webhooks let Mynth notify your backend when a task finishes, instead of requiring active polling.

## Two webhook paths

### Dashboard-managed webhooks

Create these in the [dashboard](https://mynth.io/dashboard/webhooks).

Use them when you want:

* durable account-level delivery
* signed requests
* reusable configuration across tasks

### Request-level custom webhooks

Attach these per task:

```ts theme={"theme":"kanagawa-dragon"}
webhook: {
  custom: [{ url: "https://example.com/api/mynth-webhook?token=abc123" }];
}
```

Use them when you need extra task-specific endpoints.

To skip dashboard-managed webhooks for a single task while still sending custom endpoints:

```ts theme={"theme":"kanagawa-dragon"}
webhook: {
  dashboard: false,
  custom: [
    { url: "https://example.com/api/mynth-webhook?token=abc123" }
  ]
}
```

## Important security difference

<Warning>
  Dashboard-managed webhooks are signed. Request-level custom webhook URLs are not signed, so you
  should add your own verification token in the path or query string.
</Warning>

## Events

Mynth emits image-task events:

* `task.image.generate.completed`
* `task.image.generate.failed`
* `task.image.rate.completed`
* `task.image.rate.failed`
* `task.image.alt.completed`
* `task.image.alt.failed`

Dashboard event filters also support broader task-level routing such as `task.completed`, `task.failed`, and `all`.

## Headers

Signed webhook deliveries include:

* `X-Mynth-Event`
* `X-Mynth-Signature`

The signature format is:

```text theme={"theme":"kanagawa-dragon"}
t={timestamp},v1={signature}
```

The payload is signed as:

```text theme={"theme":"kanagawa-dragon"}
{timestamp}.{raw_request_body}
```

with HMAC-SHA256.

### Verify webhook signatures

Verify the `X-Mynth-Signature` header before processing the payload:

```ts theme={"theme":"kanagawa-dragon"}
import { createHmac } from "crypto";

function verifyWebhookSignature(signatureHeader: string, rawBody: string, secret: string): boolean {
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=", 2)));
  const timestamp = parts["t"];
  const expectedSignature = parts["v1"];

  const computed = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");

  return computed === expectedSignature;
}
```

Always use the raw request body (not a parsed and re-serialized object) for verification.

Get your webhook secret from the [dashboard](https://mynth.io/dashboard/webhooks).

## Example payload

```json theme={"theme":"kanagawa-dragon"}
{
  "event": "task.image.generate.completed",
  "task": {
    "id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP"
  },
  "request": {
    "prompt": "Packaging concept for a botanical tea brand",
    "model": "google/gemini-3.1-flash-image"
  },
  "result": {
    "model": "google/gemini-3.1-flash-image",
    "images": [
      {
        "status": "success",
        "id": "img_123",
        "url": "https://...",
        "mynth_url": "https://...",
        "size": "1024x1024",
        "cost": "0.01250000"
      }
    ]
  }
}
```

## Delivery design guidance

<Tip>
  Use webhooks for durable backend workflows. Use polling when the user is waiting on a live result
  in the browser.
</Tip>

Use webhooks when:

* the result should trigger backend work
* users do not need a live progress indicator
* you want durable event-driven integration

Use polling when the browser needs immediate status updates.
