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

# Outbound webhooks

> Notify your own systems the moment a workflow execution finishes — with signed, retried deliveries you can trust

## What outbound webhooks do

An outbound webhook lets MagOneAI **push a notification to your system when a workflow execution finishes** — succeeded or failed. Instead of polling for status, your backend receives an HTTP POST the moment a run ends, so you can update a record, kick off downstream work, or alert a channel.

<Warning>
  This is the opposite direction from the [API trigger](/workflows/triggers-and-execution). The API trigger lets an external system **start** a workflow. Outbound webhooks let MagOneAI **tell** an external system that a workflow **finished**. If you want to launch a run from outside, use the trigger; if you want to be notified when a run ends, use an outbound webhook.
</Warning>

## How subscriptions work

You attach one or more **webhook subscriptions** to a use case. Each subscription has:

* A **target URL** — the HTTPS endpoint MagOneAI delivers to.
* A list of **events** it cares about.
* An optional **trigger-type filter** — only notify for runs started a certain way.
* A **signing secret** — used to sign every delivery so you can verify it came from MagOneAI.

A use case can have several subscriptions, so you can route `workflow.failed` to an alerting endpoint and `workflow.completed` to your application without them stepping on each other.

### Events

| Event                | Fires when                         |
| -------------------- | ---------------------------------- |
| `workflow.completed` | An execution finishes successfully |
| `workflow.failed`    | An execution ends in failure       |

Subscribe to one or both per subscription.

### Filtering by trigger type

A subscription can optionally filter which runs notify it by **how the run was triggered** — manual / in-app, chat, schedule, or API. Leave the filter empty to receive notifications for **all** trigger types.

<Tip>
  Use a trigger-type filter to keep noise down. For example, notify only on `schedule` runs if you only care about your unattended nightly jobs, not the manual runs you launch from Studio while testing.
</Tip>

## Set up a webhook

<Steps>
  <Step title="Open the use case's webhooks">
    Go to the use case you want to be notified about and open its webhook subscriptions.
  </Step>

  <Step title="Add a subscription">
    Enter the **target URL** MagOneAI should POST to, and choose the **events** (`workflow.completed`, `workflow.failed`, or both).
  </Step>

  <Step title="Optionally filter by trigger type">
    Pick the trigger types that should notify this subscription, or leave it empty for all of them.
  </Step>

  <Step title="Save the signing secret">
    On creation, MagOneAI shows the **signing secret once**. Copy it and store it securely (e.g. in your secret manager) — you'll need it to verify deliveries, and it isn't shown again.
  </Step>

  <Step title="Send a test event">
    Use **Send test event** to deliver a sample payload to your endpoint. Confirm your endpoint receives it, verifies the signature, and returns a 2xx before you rely on it.
  </Step>
</Steps>

<Note>
  Manage subscriptions from the use case: create, list, delete, send a test event, and view delivery history for each one.
</Note>

## The delivery payload

Each delivery is an HTTP POST with a JSON body describing the execution that finished. The exact fields may grow over time, so write your receiver to ignore unknown keys. A delivery looks roughly like this:

```json theme={null}
{
  "event": "workflow.completed",
  "execution_id": "exec_01HX...",
  "use_case_id": "uc_01HX...",
  "status": "completed",
  "trigger_type": "schedule",
  "output": {
    "summary": "Processed 42 records"
  }
}
```

* **`event`** — `workflow.completed` or `workflow.failed`.
* **`execution_id`** — the run that finished; use it to drill into [execution history](/workflows/triggers-and-execution#execution-history).
* **`use_case_id`** — the use case the run belongs to.
* **`status`** — the terminal status of the execution.
* **`trigger_type`** — how the run was started (manual / in-app, chat, schedule, API).
* **`output`** — the workflow's output for completed runs.

## Verifying deliveries

Every delivery is **signed** so your receiver can confirm it genuinely came from MagOneAI and wasn't tampered with. Each request carries these headers:

| Header                | Contents                                     |
| --------------------- | -------------------------------------------- |
| `X-MagOne-Signature`  | `t=<unix_timestamp>,v1=<hex HMAC-SHA256>`    |
| `X-MagOne-Event`      | The event name, e.g. `workflow.completed`    |
| `X-MagOne-Webhook-Id` | The subscription that produced this delivery |

The signature is computed Stripe-style: take the timestamp `t` from the header, build the signed string as the timestamp, a literal `.`, then the **raw request body**, and compute an HMAC-SHA256 of that string keyed with your subscription's **signing secret**. Compare the resulting hex digest to `v1`.

```
signed_string = "<t>." + raw_request_body
v1 = HMAC_SHA256(signing_secret, signed_string)   // compare to header, hex
```

<Warning>
  Always verify the signature **and** check that the timestamp is recent before trusting a delivery. Compute the HMAC over the **raw** body (not a re-serialized copy), compare digests in constant time, and reject deliveries whose timestamp is too old — this is what prevents an attacker from replaying a captured request. Treat any delivery that fails verification as untrusted.
</Warning>

## Reliability and delivery history

MagOneAI retries failed deliveries automatically, and records every attempt so you can see what happened.

<AccordionGroup>
  <Accordion title="How retries work" icon="rotate">
    If your endpoint doesn't return a success response, MagOneAI retries the delivery — up to **5 attempts** with **exponential backoff** spread over roughly **30 minutes**. Make your endpoint respond quickly with a 2xx once it has accepted the event; do the heavy work asynchronously so a slow handler doesn't cause needless retries.
  </Accordion>

  <Accordion title="Idempotency" icon="fingerprint">
    Because deliveries are retried, your endpoint may receive the same event more than once. Use the `execution_id` (together with the `event`) to de-duplicate, so processing the same notification twice is safe.
  </Accordion>

  <Accordion title="Delivery log" icon="list-check">
    Each attempt is logged against the subscription — including the response status code and the response body — so you can inspect delivery history when something isn't arriving. Use it to confirm test events landed, diagnose 4xx/5xx responses from your endpoint, and see whether a delivery eventually succeeded on retry.
  </Accordion>
</AccordionGroup>

## Good patterns

* **Test before you depend on it.** Send a test event and confirm your endpoint verifies the signature and returns 2xx before wiring webhooks into anything important.
* **Verify every delivery.** Never act on a payload you haven't authenticated — check the signature and the timestamp first.
* **Respond fast, process later.** Acknowledge with a 2xx immediately and handle the work asynchronously to avoid retry storms.
* **De-duplicate by `execution_id`.** Retries mean at-least-once delivery; idempotent handling keeps repeats harmless.

## Next steps

<CardGroup cols={2}>
  <Card title="Triggers & execution" icon="play" href="/workflows/triggers-and-execution">
    Start workflows from outside — the inbound counterpart to outbound webhooks
  </Card>

  <Card title="Versions & publishing" icon="code-branch" href="/workflows/versions-and-publishing">
    Which version of a workflow a triggered run resolves to
  </Card>
</CardGroup>
