> ## 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.

# Callback Payloads

> Callback JSON structure, run statuses versus callback statuses, custom_data, and examples for listing and replaying callbacks.

<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="rotate" href="/v1/runs/callback-delivery">
    Idempotency, missed-callback detection, and replaying failed deliveries.
  </Card>
</CardGroup>

## Callback Structure

All callbacks follow the same consistent format:

<Note>
  **Callback Delivery**: Both `async` and `schedule` modes deliver results via the callback URL provided in your request.

  **Consistent Format**: All execution modes use the same action logic, so inputs and results are identical regardless of mode.

  **Error Handling**: Errors follow the [standard API error format](/v1/faq-troubleshooting#troubleshooting-errors).
</Note>

```json async callback format theme={null}
{
  "callback_ref_uid": "string",
  "run": {
    "run_uid": "string",
    "batch_uid": "string",
    "status": "CREATED" || "INVALID" || "QUEUED" || "SCHEDULED" || "BLOCKED" || "STOPPED" || "RUNNING" || "FAILED" || "PARTIAL_SUCCEEDED" || "SUCCEEDED",
    "scheduled_run_uid": "string | null"
  },
  "input": {} || null,
  "custom_data": {} || null,
  "error": {} || null,
  "results": [] || null
}
```

<Info>
  To track or tag your inputs, you can attach any custom metadata to each input via the `custom_data` field. It is especially useful
  in `async` and `schedule` modes to help you get context on the results.
  This applies even when running with multiple inputs: each input will produce one or more callbacks, **each carrying its own `custom_data`**.

  This object is then **injected as-is** at the root of every callback, allowing you to correlate each result with your own data or internal references.
</Info>

### Callback Payload Fields

<ParamField body="callback_ref_uid" type="string">
  Stable callback identifier that remains the same across retries. Use this for **safe deduplication** — even if your endpoint receives multiple retries, this identifier always refers to the same logical callback event.
</ParamField>

<ParamField body="run" type="object">
  Execution context containing `run_uid` (unique to the entire run), `batch_uid` (unique to this batch), `status` (current execution state), and optionally `scheduled_run_uid` (present when the run was triggered by a schedule — use it to correlate callbacks with [scheduled runs](/v1/api/schedules/get)).
</ParamField>

<ParamField body="input" type="object | null">
  The processed input data that was used for this batch (cleaned and validated by our engine).
</ParamField>

<ParamField body="custom_data" type="object | null">
  Your custom data passed via `inputs.custom_data`. Available at the root level for easy access.
</ParamField>

<ParamField body="error" type="object | null">
  Error details if this batch failed. Follows the [standard API error format](/v1/faq-troubleshooting#troubleshooting-errors).
</ParamField>

<ParamField body="results" type="array | null">
  The actual results for this batch (null if there was an error). Format matches the specific action's output.
</ParamField>

### Callback HTTP Headers

Webhook requests include the following HTTP headers for identification and tracking:

| Header             | Description                                                                                                                  |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **X-Callback-Ref** | Stable callback identifier, identical across retries (same as `callback_ref_uid` in the payload). Use for **deduplication**. |
| **X-Run-Callback** | Unique identifier for each delivery attempt. Changes on every retry. Use for **delivery tracking**.                          |

<Tip>
  Use `X-Callback-Ref` (or `callback_ref_uid` in the payload) to detect and safely ignore duplicate callbacks. Use `X-Run-Callback` to log and debug individual delivery attempts.
</Tip>

## Understanding Run Statuses vs Callback Statuses

It's important to distinguish between two different types of statuses in the callback system:

### Run Statuses

Run statuses (found in `run.status` within the callback payload) indicate the execution state of the entire run or batch. The available run statuses are:

* **`CREATED`**: The run or schedule has been created but not yet queued for execution.
* **`INVALID`**: The run or schedule is invalid (e.g., due to bad input or configuration).
* **`QUEUED`**: The run is waiting in the queue to be executed.
* **`SCHEDULED`**: The run is scheduled to execute at a future time (applies to scheduled/CRON jobs).
* **`BLOCKED`**: The run stopped and is waiting on something that has to change — an invalid input, an identity that needs reconnecting, or a limit that has been reached. The cause is in `error_label`; see [Recovering a Blocked Run](/v1/runs/recover-blocked-run).
* **`STOPPED`**: The run was stopped before completion (manually or by the system).
* **`RUNNING`**: The run is currently in progress.
* **`FAILED`**: The run has failed. This can occur when the input seems correct, but during processing in the callback, it returns a failed status with an error (e.g., `424 – No results`). In this case, the callback itself was processed correctly, even if the page doesn't exist or returns nothing.
* **`PARTIAL_SUCCEEDED`**: The run completed with some errors, but partial results are available.
* **`SUCCEEDED`**: The run completed successfully.

### Callback Statuses

Callback statuses (used when filtering callbacks via the API) indicate the delivery status of the callback to your webhook endpoint:

* **`PENDING`**: The callback is queued and waiting to be delivered.
* **`RUNNING`**: The callback has been sent to your endpoint.
* **`FAILED`**: The callback delivery failed (network error, timeout, or your endpoint returned an error).
* **`SUCCESS`**: The callback was successfully delivered and your endpoint returned a successful response.

### Understanding BLOCKED and FAILED Run Statuses

When processing async operations, you may encounter inputs that cannot be processed. Here's how to interpret the different scenarios:

#### BLOCKED Status

A run status of `BLOCKED` means the run stopped and is waiting on something that has to change before it can continue. The cause is in `error_label`. Common ones:

* **Invalid input** — the format is wrong, or the input references a resource that doesn't exist or isn't accessible. For example a URL like `https://www.linkedin.com/products/...`, which is not a LinkedIn company page
* **The identity needs attention** — an expired session, a login challenge, or no valid account available
* **A limit was reached** — the run resumes once the window rolls over

The callback itself was delivered correctly; the run is what is blocked. [Recovering a Blocked Run](/v1/runs/recover-blocked-run) maps every `error_label` to its recovery.

#### FAILED Status (424 – No results)

A run status of `FAILED` with a `424 – No results` error indicates that:

* The input format appears correct
* During processing, no results could be found for the provided input
* The input may redirect to a 404 page on LinkedIn or the target resource doesn't exist
* **Important**: The callback itself was processed correctly by Edges, even though no results were found

<Note>
  Technically, when a callback returns `FAILED` with `424 – No results`, the callback processing itself succeeded — Edges correctly processed your request and determined that no results exist. The failure is in the data retrieval, not in the callback delivery mechanism.
</Note>

## Using `custom_data` to Track Inputs

Callbacks let you attach custom metadata to each input using the `custom_data` object.
This data is sent back in every callback, allowing you to correlate results with your internal systems.

<Steps>
  <Step title="1. Add `custom_data` to your inputs">
    When calling an action in `async` or `schedule` mode, include a `custom_data` field for each input.

    ```json theme={null}
    {
      "inputs": [
        {
          "linkedin_profile_url": "https://www.linkedin.com/in/jane-doe-1a2b3c4d/",
          "custom_data": {
            "internal_id": "12345",
            "type": "lead"
          }
        },
        {
          "linkedin_profile_url": "https://www.linkedin.com/in/john-doe-9f8e7d6c/",
          "custom_data": {
            "internal_id": "671",
            "type": "lead"
          }
        }
      ],
      "callback": {
        "url": "https://yourdomain.com/webhook",
        "headers": {
          "Authorization": "Bearer your_token"
        }
      }
    }
    ```
  </Step>

  <Step title="2. Receive `custom_data` in each callback">
    Each callback will include the same `custom_data` at the root level.
    Example for the first input:

    ```json theme={null}
    {
      "callback_ref_uid": "550e8400-e29b-41d4-a716-446655440000",
      "run": { "run_uid": "...", "batch_uid": "...", "status": "RUNNING" },
      "input": {
        "linkedin_profile_url": "https://www.linkedin.com/in/jane-doe-1a2b3c4d/"
      },
      "custom_data": {
        "internal_id": "12345",
        "type": "lead"
      },
      "results": [ { "linkedin_profile_id": "...", "full_name": "Jane Doe" } ]
    }
    ```

    And for the second input:

    ```json theme={null}
    {
      "callback_ref_uid": "550e8400-e29b-41d4-a716-446655440001",
      "run": { "run_uid": "...", "batch_uid": "...", "status": "RUNNING" },
      "input": {
        "linkedin_profile_url": "https://www.linkedin.com/in/john-doe-9f8e7d6c/"
      },
      "custom_data": {
        "internal_id": "671",
        "type": "lead"
      },
      "results": [ { "linkedin_profile_id": "...", "full_name": "John Doe" } ]
    }
    ```
  </Step>

  <Step title="3. Match and track on your side">
    You can now correlate each callback to internal data using the `custom_data` fields.
    This makes it easy to process results in your own system — even with multiple inputs and parallel callbacks.
  </Step>
</Steps>

## Examples

### Listing Callbacks

Use [`GET /runs/callbacks`](/v1/api/callbacks/list) to retrieve all callbacks with filtering and pagination.

<Expandable title="Available filters">
  * **`status`**: Filter by callback delivery status. Valid values are:
    * `PENDING`: Callbacks queued and waiting to be delivered
    * `RUNNING`: Callbacks that have been sent to your endpoint
    * `FAILED`: Callbacks that failed to be delivered (network errors, timeouts, or endpoint errors)
    * `SUCCESS`: Callbacks that were successfully delivered and acknowledged by your endpoint
  * **`run_uid`**: Filter by specific run to see all callbacks for that run
  * **`limit` & `offset`**: Pagination controls to retrieve callbacks in batches
  * **`sort`**: Sorting order (prefix with `-` for descending, e.g., `-created_at` to get newest first)
</Expandable>

<Expandable title="Example: List failed callbacks">
  ```bash theme={null}
  curl -X GET "https://api.edges.run/v1/runs/callbacks?status=FAILED&limit=20&sort=-created_at" \
    -H "Accept: application/json" \
    -H "X-API-Key: <YOUR_API_KEY>"
  ```
</Expandable>

### Getting a Specific Callback

Use [`GET /runs/callbacks/{callback_uid}`](/v1/api/callbacks/get) to fetch detailed information:

<Expandable title="Example: Get callback details">
  ```bash theme={null}
  curl -X GET "https://api.edges.run/v1/runs/callbacks/{callback_uid}" \
    -H "Accept: application/json" \
    -H "X-API-Key: <YOUR_API_KEY>"
  ```
</Expandable>

### Replaying Callbacks

Use [`POST /runs/callbacks/{callback_uid}/replay`](/v1/api/callbacks/replay) to retry failed callbacks:

<Info>
  **How replay works:**

  * Each replay creates a new callback (up to 3 total replays)
  * You can only replay the original callback, not callback responses
  * This helps you track each attempt and identify specific issues with your webhook URL

  **When to use replay:**

  * Your webhook URL was temporarily unavailable
  * You received a callback but want to retry processing
  * You need to debug callback delivery issues
</Info>

<Expandable title="Example: Replay a callback">
  ```bash theme={null}
  curl -X POST "https://api.edges.run/v1/runs/callbacks/{callback_uid}/replay" \
    -H "Accept: application/json" \
    -H "X-API-Key: <YOUR_API_KEY>" \
    -H "Content-Type: application/json"
  ```
</Expandable>

<Note>If you have a sandbox, you will have access to several workspaces. The API key will define the current workspace for the calls.</Note>
