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

# Async execution and callbacks

> How Octave's async endpoints work — submit a job, get an ID, receive results at your callback URL. Used by agents, workflows, and bulk generation.

## Why async?

Many Octave operations — running an enrichment, generating a multi-step sequence, walking a Prospector through a list, executing a multi-node workflow — take longer than a typical HTTP request should hang open. For these, Octave exposes an asynchronous pattern: you submit the job, get back a run ID, and Octave POSTs the result to a URL you control when the work is done.

The async pattern is used consistently across:

* **Async agent run** — `POST /api/v2/async/agent/run` (any saved [Agent](/concepts/agents))
* **Async email generation** — `POST /api/v2/async/headless/generate-emails` (bulk sequence generation)
* **Workflows** — `POST /api/v2/workflows/run` (multi-node [Workflows](/concepts/workflows))

All three share the same callback contract.

## The contract

1. **You POST** the job with your inputs plus a `callbackUrl` (HTTPS, reachable from the public internet).
2. **Octave returns** a run ID immediately — `agentRunOId` for agents, `workflowRunOId` for workflows.
3. **Octave executes** in the background.
4. **On completion**, Octave POSTs the result as a JSON body to your `callbackUrl`. The payload includes the run ID, status (`succeeded`, `failed`), and the agent or workflow output.
5. **You can poll** instead of (or in addition to) waiting for the callback:
   * Agents: `GET /api/v2/async/agent/run/status?agentRunOId=…`
   * Workflows: `GET /api/v2/workflows/run/status?workflowRunOId=…`

## Choosing your callback URL

The callback URL must be HTTPS and publicly reachable. Common patterns:

* **Webhook receiver** in your own app (Express endpoint, Lambda, Cloud Run function)
* **No-code receiver** like a Clay HTTP column, Zapier webhook, or n8n trigger
* **Inline polling** — set the callbackUrl to a no-op endpoint and poll status instead. This is the simplest path for quick scripts.

Your callback handler should respond `200 OK` quickly and process the payload asynchronously on your side.

## When to use sync vs async

| Situation                                       | Use                                             |
| ----------------------------------------------- | ----------------------------------------------- |
| One-off content generation, interactive testing | Sync agent run endpoints                        |
| Bulk processing, batched lists, scheduled jobs  | Async                                           |
| Multi-step pipelines                            | [Workflows](/concepts/workflows) (always async) |
| Enrich / Prospector (expensive operations)      | Async, even for single items                    |

## Error handling

Failed runs still hit your callback URL — the payload will carry `status: "failed"` and an error message. Network failures on Octave's callback POST are retried with exponential backoff; if your endpoint is consistently down, eventually the run is marked delivered-failed and visible only via the status endpoint.

If you need exactly-once semantics, dedupe on the run ID at your callback handler — Octave may retry a callback if your endpoint times out without responding.
