Skip to main content
Instead of polling GET endpoints for task status, register a webhook endpoint and MuleRouter will POST a signed callback to your URL the moment a task transitions. Every delivery is signed with HMAC-SHA256 so you can verify it came from MuleRouter and the payload has not been tampered with.

Event types

Events follow the {resource}.{action} convention: You choose which events each endpoint subscribes to. The intermediate task processing state does not emit an event. balance.low follows a one-shot edge-trigger semantics — see Billing events for details.

Register an endpoint

Webhook endpoints are managed from the MuleRouter Console. Open the Webhooks tab, click Create endpoint, and provide:
  • URL — an HTTPS URL that can accept POST requests (max 2048 characters).
  • Events — one or more of the event types above.
  • Description — optional, up to 200 characters.
On creation, the console shows the full signing secret (whsec_...) exactly once. Save it to a secret store immediately — it is never displayed again.
Each user can register up to 5 webhook endpoints. If you need to deliver to more destinations, use a single endpoint that fans out on your side.

Delivery payload

Every delivery is a POST with Content-Type: application/json. All events share the same envelope:
The data shape depends on the event family:
If you write a single dispatcher that handles both families, branch on the type field’s prefix (task. vs balance.) before reading data fields. The two shapes are intentional — task.* keeps backward compatibility with earlier releases, while balance.* introduces a data.user_id + data.payload.* convention so future balance.depleted / balance.topped_up events can reuse the same outer locator.

Task events

Task events use the flat data shape. The inner data.payload is the same JSON you would receive from the synchronous GET /vendors/.../generation/{task_id} endpoint at that point in the task lifecycle. That means your delivery handler can share result-parsing code with your polling handler, if you have one.

task.created

Fired when the task is accepted. payload only contains task_info.

task.succeeded

Fired when the task completes successfully. payload contains the full task response, including whatever fields the endpoint would normally return (e.g. images, videos, audios).

task.failed

Fired when the task fails. Error details live on payload.task_info.error, using the same shape as the MuleRouter error object.

Billing events

Billing events use the nested data shape with data.user_id for routing and data.payload for business fields. They cover wallet-state changes that don’t belong to any specific task.

balance.low

Fired when your wallet’s available balance crosses below the system threshold (default $10) following a charge. This is an edge-trigger event — it fires once when the balance drops past the threshold and stays silent until your balance recovers above the threshold and drops below it again.
Trigger semantics
  • Low balance detected: when your available_balance drops below trigger_threshold, you’ll receive a balance.low delivery shortly after.
  • Sustained low: while you remain below threshold without a top-up, deliveries are throttled — you won’t be spammed with repeated notifications.
  • Top-up then drop again: if you top up above the threshold and later drop below again, you’ll receive a fresh delivery without waiting out the throttle window.
  • Healthy recovery: once your balance stays above the threshold, no further deliveries fire until it drops again.
When balance.low does not fire
  • You don’t have an active webhook endpoint subscribed to balance.low.
  • Your available_balance is at or above the trigger_threshold.
  • A recent delivery for the same wallet was already sent and your balance hasn’t recovered above the threshold in between.
  • The triggering activity is only a hold (frozen reservation) for an asynchronous task — balance.low fires when funds are actually settled, not when they’re held. This avoids false positives if the task later fails and the hold is released.
Recommended client behavior
  • Use this event as a hint to surface a low-balance banner in your UI, send your user a “top up” reminder, or pause non-critical workloads — not as the sole source of truth for your wallet balance.
  • Always verify the current balance via your wallet’s source of truth before acting on financial decisions, since available_balance in the payload is a snapshot at trigger time.

Request headers

Each delivery includes the following HTTP headers:

Verify the signature

The signed content is three parts joined with .:
The signature is computed as:
The header value carries a version prefix so we can upgrade the algorithm in the future without breaking existing receivers:
Verify signatures against the raw request body, not a re-serialized JSON object. Re-encoding the payload will change byte-level whitespace and key ordering, and your computed signature will not match.
Here is a complete receiver in Python:

Security

Delivery and retries

If the target URL does not return a 2xx within the timeout, MuleRouter retries with exponential backoff: After 5 retries (6 total attempts), the delivery is marked failed and no further attempts are made for that event.
Every attempt of the same delivery reuses the same X-MuleRouter-Webhook-Id. The timestamp and signature are recomputed each time, so your receiver must always recompute the signature from the headers it actually received — do not cache.

Idempotency

Two IDs cooperate to make retries safe:
  • evt_... (event ID) — lives on the payload’s top-level id field, identifies “something happened in MuleRouter”.
  • whd_... (delivery ID) — lives on the X-MuleRouter-Webhook-Id header, identifies “an attempt to deliver that event to this endpoint”. It is unique per (event, endpoint) and is reused across retries of the same delivery.
Use whd_... as your deduplication key: store it when you first successfully process a delivery, and short-circuit if you see it again. This is the standard way to handle retries without double-processing.

Rate limits and quotas

Next steps

  • Open the Webhooks tab in the Console to register your first endpoint.
  • Use the Send test event button on any endpoint to trigger a synthetic task.succeeded delivery (id prefixed with evt_test_) and verify your signature-verification logic end-to-end before sending real traffic.