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

# Callback Delivery and Webhook Triggers

> Start workflow deployments with API invoke or webhook triggers, send results to callback URLs, and verify signed callback delivery

Fetch Hive separates how a run starts from how the result comes back:

* **API invoke** starts a run through `POST /v1/workflow/invoke` with a workspace API key.
* **Webhook trigger** starts a deployed variant through `POST /v1/workflow/webhooks/...` with the deployment webhook secret.
* **Callback delivery** returns immediately and later sends a signed completion event to `async.callback_url`.

The request field is still named `async` for API compatibility, but the product behavior is callback delivery.

## Overview

The workflow deployment helper exposes API invocation and webhook triggering for the selected deployment variant. Webhook triggers use the deployment webhook secret and do not require a workspace API key.

API invoke requests can choose direct delivery or callback delivery. When you include `async.callback_url`, Fetch Hive returns immediately and later delivers a signed callback event to that URL.

Webhook triggers always use callback delivery. They require `async.callback_url`, return quickly, and deliver the final result only through the signed callback event.

The same deployment secret is used in both directions. A caller proves it can start the deployed workflow by sending the secret in `X-Fetch-Hive-Webhook-Secret`. Your callback receiver proves the later event came from Fetch Hive by verifying `X-Fetch-Hive-Signature` with HMAC SHA256 and that same secret.

## How do I trigger a workflow by webhook? <a href="#trigger-workflow-webhook" id="trigger-workflow-webhook" />

Open **More** in the workflows sidebar and click **Get Code**, or open **Code Snippet** from a workflow deployment page.

Select the **Deployment** and **Variant** you want to trigger, then open the **Webhook trigger** tab.

Copy the webhook URL and send a `POST` request with the deployment secret in `X-Fetch-Hive-Webhook-Secret`:

```bash theme={null}
curl 'https://api.fetchhive.com/v1/workflow/webhooks/YOUR_WORKFLOW_ENDPOINT_ID/variants/YOUR_VARIANT_ID' \
  -H 'X-Fetch-Hive-Webhook-Secret: YOUR_WEBHOOK_SECRET' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "inputs": {
      "topic": "State of enterprise AI in 2026"
    },
    "async": {
      "callback_url": "https://example.com/callback"
    }
  }'
```

If your webhook provider cannot send custom headers, you can pass `?secret=YOUR_WEBHOOK_SECRET` as a fallback. Prefer the header whenever the provider supports it.

Use the deployment webhook secret shown in the helper. This is the same secret Fetch Hive uses to sign callback delivery events for this deployment.

When the body has an `inputs` object, Fetch Hive uses that object as the workflow start inputs. If the body is a JSON object without `inputs`, Fetch Hive treats the whole object as workflow inputs.

You can also include optional `user` and `metadata` fields beside `inputs`. The `async.callback_url` field is required:

```json theme={null}
{
  "inputs": {
    "topic": "State of enterprise AI in 2026"
  },
  "user": {
    "id": "user_123",
    "email": "person@example.com"
  },
  "metadata": {
    "source_system": "zapier"
  },
  "async": {
    "callback_url": "https://example.com/callback"
  }
}
```

`metadata` is caller-defined user metadata for audit and log filtering. It must be a flat object whose values are strings, numbers, booleans, or `null`; nested objects and arrays return a validation error before the run starts. To send metadata, use the wrapped body shape shown above with `metadata` beside `inputs`. If you omit `inputs`, the whole JSON object is treated as workflow inputs, so `metadata` is not extracted as log-filter metadata.

Webhook-triggered runs appear under **Source: Webhook** in workflow logs and show **Delivery: Callback** with callback delivery logs.

## How do I enable callback delivery for API invoke? <a href="#generate-callback-request" id="generate-callback-request" />

Open **More** in the workflows sidebar and click **Get Code**, or open **Code Snippet** from a workflow deployment page.

Select the **Deployment** and **Variant** you want to invoke.

Turn on **Callback delivery**.

The cURL snippet updates to include:

```json theme={null}
{
  "async": {
    "enabled": true,
    "callback_url": "https://example.com/callback"
  }
}
```

Replace the example callback URL with your real webhook endpoint before you run the request.

## How do I verify callback signatures? <a href="#verify-webhook-signatures" id="verify-webhook-signatures" />

Turn on **Callback delivery** in the code snippet dialog, or open the **Webhook trigger** tab.

Copy the **Signing secret** shown below the snippet, or view the same secret from the workflow deployment details page.

When your server receives the callback event, verify the `X-Fetch-Hive-Signature` header using HMAC SHA256 and that signing secret.

Treat any callback event that fails signature verification as untrusted.

If the workflow was started by the webhook trigger, this signing secret is the same value the triggering system sent in `X-Fetch-Hive-Webhook-Secret`. The inbound trigger check is a bearer-style secret check; the outbound callback check is HMAC signature verification.

## What response do I get from callback delivery? <a href="#callback-response" id="callback-response" />

When callback delivery is enabled, the public route returns immediately instead of waiting for the workflow output.

For direct delivery, the public API waits only within the sync timeout window.
If a workflow exceeds that window, Fetch Hive requests cancellation and returns
`504` with the `request_id` and `run_status: "failed"`. Use callback delivery
for long-running workflows or workflows that include Human in the Loop steps.

The API invoke route can return a `running` state:

```json theme={null}
{
  "request_id": "req_019d52846ea37682b03522fd0695cc43",
  "run_status": "running",
  "webhook_secret": "YOUR_WEBHOOK_SECRET"
}
```

At high concurrency, it can also return a `queued` state:

```json theme={null}
{
  "request_id": "req_019d52846ea37682b03522fd0695cc43",
  "run_status": "queued",
  "webhook_secret": "YOUR_WEBHOOK_SECRET"
}
```

Use the returned `request_id` for your own request tracking, and rely on callback delivery for the final result.

Workflow webhook triggers also return quickly:

```json theme={null}
{
  "request_id": "req_019d52846ea37682b03522fd0695cc43",
  "run_status": "running"
}
```

## What validation rules apply to callback delivery? <a href="#callback-validation" id="callback-validation" />

If `async.enabled` is `true`, `callback_url` is required.

Webhook triggers always require `async.callback_url`.

Fetch Hive also validates that `callback_url` is a valid URL.

Workflows that contain a Human in the Loop step always require callback delivery, even for API invoke requests. They cannot complete synchronously because Fetch Hive pauses the workflow until the selected workspace member responds.

For example, these errors are explicitly confirmed:

```json theme={null}
{
  "error": "callback_url is required when async is enabled"
}
```

```json theme={null}
{
  "error": "callback_url must be a valid URL"
}
```

## What callback events does Fetch Hive emit? <a href="#webhook-events" id="webhook-events" />

Every callback delivery has the same envelope:

```json theme={null}
{
  "event_type": "<event_type>",
  "request_id": "req_...",
  "data": { "...": "..." }
}
```

The `event_type` field tells you which terminal state the workflow reached:

* `workflow.completed` - the workflow finished successfully. `data.output` contains the final output value.
* `workflow.failed` - the workflow errored during execution. `data.error` contains the error message.
* `workflow.cancelled` - the workflow was cancelled before it could complete, either by a user in the Fetch Hive dashboard or directly via the orchestrator. `data.error` contains the cancellation reason, and `data.reason` is always the string `"cancelled"` so you can branch without parsing the message.

For image-generation workflows, `workflow.completed` includes a structured output object:

* `data.output.settings` (model and settings used)
* `data.output.assets` (generated uploaded assets)

Example `workflow.cancelled` payload:

```json theme={null}
{
  "event_type": "workflow.cancelled",
  "request_id": "req_019d52846ea37682b03522fd0695cc43",
  "data": {
    "error": "Workflow cancelled: Activity canceled",
    "reason": "cancelled"
  }
}
```

Treat `workflow.cancelled` as a terminal state on your side. No further callback event will be delivered for that `request_id`. The workflow run's partial progress, including any completions that finished before the cancel, is still available via the run detail view and the [Logs](./logs) endpoints.

## Where do I inspect callback delivery after it starts? <a href="#inspect-callback-activity" id="inspect-callback-activity" />

Use [Logs](./logs) after the workflow starts.

The workflow run detail view can show the **Callback** badge, **Callback delivery** section, output, and **Webhook Logs** for runs that used callback delivery. **Webhook Logs** are callback delivery attempts, not the run source.

If callback delivery fails, the logs view can also show retry history and response details for each delivery attempt.

## Notes

Webhook triggers, API invoke requests with callback delivery, and signed callback events are separate parts of the workflow lifecycle. A webhook trigger starts a run. Callback delivery controls whether Fetch Hive returns immediately and stores a callback URL. The signed callback event is how the final result reaches that callback URL later.

If you do not need callback delivery, keep it disabled and use the normal direct response documented in [Run with API](./run-with-api).

See also: [Run with API](./run-with-api), [Logs](./logs), and [Error Handling](./error-handling)
