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

# Callbacks

> Set up async and schedule webhooks, choose streaming or final delivery, and understand data completeness guarantees.

<CardGroup cols={3}>
  <Card title="Overview" icon="book" href="/v1/runs/callbacks">
    What callbacks are, streaming vs final delivery, and data completeness guarantees.
  </Card>

  <Card title="Payloads" icon="brackets-curly" href="/v1/runs/callback-payloads">
    Callback JSON structure, run vs callback statuses, custom\_data, and examples.
  </Card>

  <Card title="Delivery" icon="inbox" href="/v1/runs/callback-delivery">
    Idempotency, missed-callback detection, and replaying failed deliveries.
  </Card>
</CardGroup>

## What are Callbacks?

Callbacks are the way Edges delivers results for `async` and `schedule` execution modes.
Instead of waiting for the entire operation to complete, you receive results progressively as they become available.

<Tip>
  **When to use callbacks:** Choose `async` or `schedule` mode when you need to process large datasets, want to consume data in parallel, or need to handle long-running operations without timeouts.
</Tip>

<Info>
  Callbacks are only useful if you can **trust that all of them are received and processed**.
  To ensure you never miss any data, see [Managing Callbacks](/v1/runs/callback-delivery#managing-callbacks).
</Info>

Understanding callback statuses is essential for handling callbacks correctly. Here are quick definitions:

* **`PENDING`**: The callback is queued and waiting to be delivered to your webhook endpoint.
* **`RUNNING`**: The callback has been sent to your endpoint and is being processed (or was successfully delivered).
* **`FAILED`**: The callback delivery failed (e.g., network error, timeout, or your endpoint returned an error status code).
* **`SUCCESS`**: The callback was successfully delivered to your endpoint and your endpoint returned a successful HTTP status code (2xx).

<Note>
  These are **callback statuses** that indicate the delivery status of the callback itself. They are different from **run statuses** (like `BLOCKED`, `FAILED`, `SUCCEEDED`) which indicate the execution state of the run. See [Understanding Run Statuses vs Callback Statuses](/v1/runs/callback-payloads#understanding-run-statuses-vs-callback-statuses) for more details.
</Note>

## Quick Start

### 1. Set up your webhook endpoint

Create an HTTPS endpoint that can receive POST requests with JSON payloads.

### 2. Configure your action call

Include a `callback` parameter with your webhook URL:

```json theme={null}
{
  "inputs": [...],
  "callback": {
    "url": "https://yourdomain.com/webhook",
    "headers": {
      "Authorization": "Bearer your_token"
    },
    "on": "all"
  }
}
```

<Tip>
  Set `"on": "final"` instead of `"all"` if you only want to receive a single callback when the job completes. With `"final"` mode, the callback indicates the run status, and you then fetch results using [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs). This is ideal for automation tools like n8n, Make, or Zapier. See [Streaming vs Final Callbacks](#streaming-vs-final-callbacks) for details.
</Tip>

### 3. Handle incoming callbacks

* **For `on: "all"` mode:** Process the JSON payloads as they arrive. Each callback contains results and metadata. Verify completeness by comparing the run output count with received results.
* **For `on: "final"` mode:** Receive a single callback with run status, then fetch all results using [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs).

For detailed setup and management, see the sections below.

## Understanding Async and Schedule Modes

`async` and `schedule` execution modes are both async modes: the results are not sent immediately in the response payload but delivered during execution via callbacks.

<Tip>
  `schedule` is built on top of `async` and only adds ways to postpone and schedule calls. Both modes share the same callback delivery mechanism. For a detailed comparison and guidance on when to use each mode, see [When to use live, async or schedule modes.](/v1/runs/overview).
</Tip>

### Setting Up Callbacks

Every action call in async/schedule mode requires a `callback` parameter:

<ParamField body="url" type="string">
  Your webhook URL that will receive the callback data. Must be HTTPS.
</ParamField>

<ParamField body="headers" type="object">
  Optional headers for authentication or custom metadata (API keys, etc.).
</ParamField>

<ParamField body="on" type="string" default="all">
  Defines when you want to receive callbacks. See [Streaming vs Final Callbacks](#streaming-vs-final-callbacks) below.

  * `"all"` (default): Stream callbacks progressively as results are processed
  * `"final"`: Receive a single callback when all inputs are processed or an error occurs
</ParamField>

<Note>Please refer to each async/schedule action in the [API Reference](/v1/api/introduction) for specific details.</Note>

## Streaming vs Final Callbacks

By default, Edges sends callbacks progressively as results become available (`callback.on: "all"`). However, you can opt to receive only a single callback when the entire job completes by setting `callback.on: "final"`.

### When to Use Each Mode

| Mode | `callback.on` | Behavior | Best For |
| - | - | - | - |
| **Streaming** | `"all"` (default) | Multiple callbacks as results are processed | Real-time processing, large datasets, progressive data consumption |
| **Final** | `"final"` | Single callback when job completes | Automation tools (n8n, Make, Zapier), simple integrations, cost optimization |

### Streaming Mode (`"all"`)

This is the default behavior. You receive callbacks progressively:

* One callback per batch/page of results with `run.status: RUNNING`
* One final callback with `run.status: SUCCEEDED` when complete

```json theme={null}
{
  "inputs": [...],
  "callback": {
    "url": "https://yourdomain.com/webhook",
    "on": "all"
  }
}
```

**Advantages:**

* Start processing data immediately as it arrives
* Better for large datasets (stream instead of waiting)
* Real-time progress tracking

**Considerations:**

* Your endpoint receives multiple requests per run
* Requires handling multiple callbacks and aggregating results
* Must verify completeness by comparing output count with received results

**Verifying Completeness (All Callbacks Mode):**

After receiving the final callback with `run.status: SUCCEEDED`, verify that all callbacks were received:

```javascript theme={null}
// 1. Get the run details to retrieve output_count
const runResponse = await fetch(`https://api.edges.run/v1/runs/${run_uid}`, {
  headers: { 'X-API-Key': 'your_api_key' }
});
const run = await runResponse.json();
const expectedOutputCount = run.output_count;

// 2. Count all results received in callbacks
// (You should track this as callbacks arrive)
const receivedResultsCount = /* sum of all results.length from callbacks */;

// 3. Compare counts
if (receivedResultsCount === expectedOutputCount) {
  console.log('✅ All callbacks received - data is complete');
} else {
  console.warn(`Missing callbacks: Expected ${expectedOutputCount}, received ${receivedResultsCount}`);
  // Fetch missing results or replay callbacks
  const outputs = await fetch(`https://api.edges.run/v1/runs/${run_uid}/outputs`, {
    headers: { 'X-API-Key': 'your_api_key' }
  });
  // Process missing results
}
```

### Final Mode (`"final"`)

You receive a single callback only when the job is complete:

* One callback with `run.status: SUCCEEDED` or `run.status: PARTIAL_SUCCEEDED` indicating the run status
* Or one callback with `run.status: FAILED` if an error occurred
* **Important:** The callback indicates the run status but does not contain all results. You must fetch results using the [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs) API endpoint.

```json theme={null}
{
  "inputs": [...],
  "callback": {
    "url": "https://yourdomain.com/webhook",
    "on": "final"
  }
}
```

**How it works:**

1. Receive final callback with run status (`SUCCEEDED`, `PARTIAL_SUCCEEDED`, or `FAILED`)
2. Fetch all results using `GET /runs/{run_uid}/outputs` API endpoint
3. Process the complete result set

**Advantages:**

* Dramatically reduces webhook executions (1 instead of N)
* Simpler integration logic (no aggregation needed)
* Lower costs for webhook-based automation platforms
* Data completeness guaranteed by the GET Results API

**Considerations:**

* Must wait for the entire job to complete before receiving status
* Requires an additional API call to fetch results after the callback

**Fetching Results (Final Callback Mode):**

After receiving the final callback, fetch all results:

```javascript theme={null}
// 1. Receive final callback with run.status
// Example callback payload:
// { "run": { "run_uid": "...", "status": "SUCCEEDED" }, ... }

// 2. Fetch all results using the GET API
const outputsResponse = await fetch(
  `https://api.edges.run/v1/runs/${run_uid}/outputs?limit=100&offset=0`,
  { headers: { 'X-API-Key': 'your_api_key' } }
);
const outputs = await outputsResponse.json();

// 3. Process the complete result set
// Note: Use pagination (limit/offset) if there are many results
```

<Tip>
  **For automation tools like n8n, Make, or Zapier:** Use `callback.on: "final"` to avoid flooding your workflows with multiple webhook triggers. This can significantly reduce your automation platform costs, especially for jobs with many results.
</Tip>

## Data Completeness Guarantees

Each callback mode ensures data completeness differently. Understanding these guarantees helps you build robust integrations that never miss data.

### All Callbacks Mode (`on: "all"`)

Results are delivered **progressively** through multiple callbacks during the run execution.

**How completeness is ensured:**

1. Each callback contains a subset of the run results
2. When the run finishes, verify completeness by:
   * Retrieving the **run output count** using [`GET /runs/{run_uid}`](/v1/api/runs/get) - the `output_count` field indicates the total number of outputs generated
   * Comparing it with the **number of results received via callbacks** (count all results from callbacks with `run.status: RUNNING` and the final `run.status: SUCCEEDED`)

**Outcome:**

* ✅ **Counts match** → All callbacks were received, data is complete
* **Counts differ** → One or more callbacks are missing
  * Use the [List Callbacks endpoint](/v1/api/callbacks/list) to identify missing callbacks
  * Retry missing callbacks using the [Replay Callback endpoint](/v1/api/callbacks/replay)
  * Or fetch missing results directly via [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs)
  * To recover input-level errors (e.g., 404s) that aren't in outputs, use [`GET /runs/{run_uid}/inputs`](/v1/api/runs/inputs)

**Guarantee:**
Data completeness is validated by a **final comparison between received callbacks and the run output count**.

### Final Callback + GET Results Mode (`on: "final"`)

Results are delivered **after the run is fully completed** using a two-step process.

**How it works:**

1. A single **final callback** indicates the run status (`SUCCEEDED`, `PARTIAL_SUCCEEDED`, or `FAILED`)
2. The client then retrieves all results using the [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs) API call

**Outcome:**

* Results are fetched directly from the server via the API
* No intermediate callbacks are involved
* No callback verification needed

**Guarantee:**
Data completeness is ensured by the **GET Results API**, not by callbacks.

### Summary

| Mode | Result Delivery | Completeness Check |
| - | - | - |
| **All Callbacks** (`on: "all"`) | Multiple callbacks during execution | Final comparison with run output count |
| **Final Callback + GET** (`on: "final"`) | One final callback + API retrieval | No callback verification needed |

### Which Mode Should I Use?

* **All Callbacks** → If you need real-time processing or progressive results
* **Final Callback + GET** → If you prefer a simpler flow and can wait for completion

## How Callbacks Work

Understanding how callbacks are processed and delivered helps you build robust integrations.

### Parallel Processing & Pagination

Async actions are designed to scale efficiently by processing multiple pages concurrently:

* **Improved performance**: Pages are processed in parallel
* **Manageable payloads**: Each page generates a separate callback
* **Faster time-to-first-result**: Start consuming data immediately

### Callback Flow

The callback flow depends on the `callback.on` setting:

<Tabs>
  <Tab title="Streaming Mode (on: all)">
    <Steps>
      <Step title="Trigger Action">
        Action is triggered in async mode with `max_results: 100`
      </Step>

      <Step title="Auto-Pagination">
        Backend automatically paginates results with `page_size: 10`
      </Step>

      <Step title="Receive Running Callbacks">
        You receive 10 callbacks with:

        * `run.status: RUNNING` (this is the run status, indicating the run is in progress)
        * Each containing \~10 results (may vary slightly as we filter out ads and other content)
      </Step>

      <Step title="Final Success Callback">
        Once the run completes, you receive one final callback with `run.status: SUCCEEDED` (indicating the run finished successfully)
      </Step>
    </Steps>

    <Info>
      **Important:** You may receive multiple callbacks for a single execution, and they may arrive out of order due to parallel processing. Always use the `run_uid` and `batch_uid` to track and organize your data.
    </Info>
  </Tab>

  <Tab title="Final Mode (on: final)">
    <Steps>
      <Step title="Trigger Action">
        Action is triggered in async mode with `max_results: 100`
      </Step>

      <Step title="Processing">
        Backend processes all results. No callbacks are sent during execution.
      </Step>

      <Step title="Final Callback">
        Once all inputs are processed, you receive **one single callback** with:

        * `run.status: SUCCEEDED` or `PARTIAL_SUCCEEDED` (indicating the run completed)
        * Or `run.status: FAILED` if an error occurred
        * **Note:** The callback indicates status but does not contain all results
      </Step>

      <Step title="Fetch Results via API">
        After receiving the final callback, fetch all results using:

        * [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs) to retrieve the complete result set
        * Results are fetched directly from the server, ensuring data completeness
      </Step>
    </Steps>

    <Info>
      **Two-step process:** With final mode, you receive exactly one callback per run (status only), then fetch results via the API. This makes it ideal for automation workflows that trigger on webhook events and prefer fetching data on-demand.
    </Info>
  </Tab>
</Tabs>

## Next Steps

<CardGroup cols={2}>
  <Card title="Callback Payloads" icon="brackets-curly" href="/v1/runs/callback-payloads">
    The JSON structure, run vs callback statuses, and `custom_data`.
  </Card>

  <Card title="Callback Delivery" icon="inbox" href="/v1/runs/callback-delivery">
    Idempotency, missed-callback detection, and replays.
  </Card>

  <Card title="List Callbacks" icon="rectangle-code" href="/v1/api/callbacks/list">
    Endpoint reference — parameters, filters and responses.
  </Card>

  <Card title="Manage Runs" icon="sliders" href="/v1/runs/manage">
    Read, stop, continue and resume a run.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.