Skip to main content
Use webhooks when a finished task should trigger backend work — persist results, update records, moderate, or fan out to other systems. Mynth POSTs a JSON payload to your endpoint when a task completes or fails. For live browser progress, use Async and polling or Browser Polling instead. You can combine both: poll for the UI and still receive webhooks for durable server-side handling. Event names, headers, and full payload shapes are listed in the Webhooks reference.

Register a dashboard webhook

Dashboard-managed webhooks are the default production path. They are reusable across tasks and signed with HMAC-SHA256.
  1. Open the webhooks dashboard.
  2. Create a webhook with your HTTPS endpoint and the events you care about.
  3. Save the signing secret when it is shown — you need it to verify deliveries.
Once enabled, matching tasks send webhooks automatically. You do not need a webhook field on each generate request.
const task = await mynth.image.generate({
  prompt: "Packaging concept for a botanical tea brand",
  model: "google/gemini-3.1-flash-image",
});

// Task is accepted; your endpoint receives the result when it finishes
console.log(task.id);

Handle a delivery

Every delivery is an HTTP POST with Content-Type: application/json.
HeaderPresent onMeaning
X-Mynth-EventAll deliveriesSpecific event name (for example task.image.generate.completed)
X-Mynth-SignatureDashboard-managed webhooks onlyHMAC-SHA256 signature in t=...,v1=... format
Content-TypeAll deliveriesapplication/json
Respond with a 2xx status as soon as you accept the payload. Treat deliveries as at-least-once: make handlers idempotent using task.id.

Completed generate task

{
  "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"
      }
    ]
  }
}

Failed generate task

Failed tasks include event, task, and request. They do not include result.
{
  "event": "task.image.generate.failed",
  "task": {
    "id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP"
  },
  "request": {
    "prompt": "Packaging concept for a botanical tea brand",
    "model": "google/gemini-3.1-flash-image"
  }
}
request is the task request as stored by Mynth (including metadata when you set it). See Use Metadata. For rate and alt payloads, see the Webhooks reference.

Verify webhook signatures

Dashboard-managed deliveries include X-Mynth-Signature. Verify it before trusting the body. Header format:
t={timestamp},v1={signature}
Signed message:
{timestamp}.{raw_request_body}
Use HMAC-SHA256 with your webhook secret. Always verify against the raw request body — not a parsed and re-serialized object.
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(
  signatureHeader: string,
  rawBody: string,
  secret: string,
): boolean {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.split("=", 2)),
  );
  const timestamp = parts["t"];
  const expected = parts["v1"];
  if (!timestamp || !expected) return false;

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

  const expectedBuf = Buffer.from(expected, "utf8");
  const computedBuf = Buffer.from(computed, "utf8");
  if (expectedBuf.length !== computedBuf.length) return false;

  return timingSafeEqual(expectedBuf, computedBuf);
}
Get the secret from the webhooks dashboard when you create the webhook. For Convex, use @mynthio/sdk/convex — see Convex integration.

Attach request-level custom webhooks

On image.generate only, you can add up to 5 extra endpoints per task with webhook.custom. These are useful for task-specific or temporary URLs.
const task = await mynth.image.generate({
  prompt: "Packaging concept for a botanical tea brand",
  model: "google/gemini-3.1-flash-image",
  webhook: {
    custom: [{ url: "https://example.com/api/mynth-webhook?token=abc123" }],
  },
});
Custom endpoints are not signed. Put your own verification token in the path or query string (as above), or use another shared secret your backend checks. To skip dashboard-managed webhooks for one task while still sending custom endpoints:
const task = await mynth.image.generate({
  prompt: "Packaging concept for a botanical tea brand",
  model: "google/gemini-3.1-flash-image",
  webhook: {
    dashboard: false,
    custom: [{ url: "https://example.com/api/mynth-webhook?token=abc123" }],
  },
});
ConstraintLimit
Custom endpoints per generate task1 to 5
Custom endpoint shape{ url: string }
Signature on custom endpointsNone — add your own token or secret check
webhook on rate / alt requestsNot supported; use dashboard webhooks

Choose events

Mynth delivers these concrete events:
EventWhen it fires
task.image.generate.completedImage generation task succeeded
task.image.generate.failedImage generation task failed
task.image.rate.completedStandalone rating task succeeded
task.image.rate.failedStandalone rating task failed
task.image.alt.completedAlt text task succeeded
task.image.alt.failedAlt text task failed
When you subscribe in the dashboard, you can also use broader filters:
SubscriptionMatches
task.completedAny *.completed event above
task.failedAny *.failed event above
allEvery event
The X-Mynth-Event header and payload.event always use the specific event name (for example task.image.generate.completed), even if the webhook was subscribed with task.completed or all. Generation-time content rating (rating on generate) does not emit task.image.rate.*. Rating is attached to the generate result; see Use Content Rating.

Delivery retries

Mynth retries failed deliveries (non-2xx or network errors) with exponential backoff and jitter, up to 12 attempts. For dashboard-managed webhooks, consecutive failures are tracked. After 30 consecutive failed delivery attempts, Mynth disables that webhook. Fix the endpoint, then re-enable it in the dashboard. Custom request-level endpoints are not auto-disabled; retries still apply for that job.

REST example

curl https://api.mynth.io/image/generate \
  -X POST \
  -H "Authorization: Bearer $MYNTH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Packaging concept for a botanical tea brand",
    "model": "google/gemini-3.1-flash-image",
    "webhook": {
      "custom": [
        { "url": "https://example.com/api/mynth-webhook?token=abc123" }
      ]
    }
  }'
If you already have a dashboard webhook subscribed to generate events, omit webhook and rely on that registration.

When to use webhooks vs polling

ApproachBest for
WebhooksBackend workflows, durable pipelines, cross-system sync
PollingBrowser previews, live progress, short user sessions
BothUI waits on poll; server persists via webhook

Next steps