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

# Overview of Execution Modes

> When to use live, async or schedule modes.

Edges supports multiple ways to execute actions and workflows, including synchronous runs, asynchronous runs, and scheduled executions. This section explains the different execution modes and how to choose the right one for your use case.

* **Synchronous Runs:** Immediate execution, results returned in real-time.
* **Asynchronous Runs:** Execution happens in the background, results can be fetched later.
* **Scheduled Runs:** Actions are scheduled to run at a specific time or on a recurring basis.

Here is a quick overview of each mode to help you decide which one fits your needs:

| Execution Mode | Data Delivery                     | When to Use                                        |
| -------------- | --------------------------------- | -------------------------------------------------- |
| **Live**       | Immediate (synchronous)           | Real-time workflows, single inputs, few results    |
| **Async**      | Background (via callback stream)  | Batch jobs, large datasets, automated pagination   |
| **Schedule**   | Recurring (via cron) or postponed | Scheduled runs, regular syncs, postponed execution |

For detailed pagination handling, see the [Pagination Guide](/v1/runs/pagination). To learn more about managing runs and schedules, see the following sections.

## Execution Statuses

When running actions in async or schedule mode, the following statuses may be encountered:

* **`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 is blocked because the input is invalid (bad input). For example, certain URLs like `https://www.linkedin.com/products/...` do not correspond to a valid LinkedIn company page. The callback itself is processed correctly, but the input cannot be processed.
* **`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.

## Live Mode

You receive data immediately as it is processed, making this the easiest mode to implement for quick integrations.

**Key characteristics:**

* **Synchronous execution** - results returned in the API response
* **Manual pagination** - you handle pagination using cursor and response headers
* **Real-time processing** - fastest time to first result
* **Rate limit handling** - you implement retry logic for `429 Too Many Requests` responses
* **Your pacing at scale** - live calls hit LinkedIn directly without async/schedule orchestration; follow [Live mode: safe practices](/v1/runs/live-mode-safe-practices) for spacing, sequencing, and jitter

<Info>
  Live mode is ideal for real-time workflows where you need immediate results and can handle pagination manually.
  See the [Pagination Guide](/v1/runs/pagination) for detailed pagination handling.
</Info>

## Async Mode

When you run an action asynchronously, the execution happens in the background and results are delivered via callbacks.

**Key characteristics:**

* **Background execution** - non-blocking, your application continues running
* **Automatic pagination** - Edges handles pagination internally
* **Progressive delivery** - results streamed via callbacks as they become available
* **Callback-based** - results delivered to your webhook URL
* **Batch processing** - ideal for large datasets and long-running tasks
* **Automatic retries** - transient errors are retried (see below)

<Info>
  Async and schedule runs automatically retry on transient errors. Each run may be attempted up to **11 times** in total (initial attempt + 10 retries).
</Info>

<Info>
  Async mode is ideal for long-running tasks, batch processing, or when you want to avoid blocking your application while waiting for results.
  Results are delivered progressively via multiple callbacks, allowing you to start processing data as soon as it becomes available.
</Info>

<Tip>
  **Prefer polling over callbacks?** You can retrieve outputs directly via [`GET /runs/{run_uid}/outputs`](/v1/api/runs/outputs) without setting up a webhook endpoint. See [Rate Limits](/v1/runs/rate-limits#run-outputs) for polling limits.
</Tip>

<Warning>
  You can attach any metadata to each input using the `custom_data` object. This data is sent in every callback, allowing you to
  correlate results with your backend.

  * Each callback relates to **a single input**, so `custom_data` is included at the **root level** of the callback payload
  * Avoid sending large or nested structures: most use cases are covered with a single internal ID
  * Keep the payload **reasonably small** to avoid hitting webhook size limits
</Warning>

### Callbacks Structure

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

To learn more about callbacks, please take a look at [Managing Callbacks](/v1/runs/callbacks).

### Available Endpoints for Runs

You can manage and monitor your runs using the following endpoints:

| Action          | Method | Endpoint                                               | Description                                                                                                          |
| --------------- | ------ | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| List all runs   | `GET`  | [`/v1/runs`](/v1/api/runs/list)                        | Retrieve a list of all runs, with filtering and pagination options.                                                  |
| Get a run       | `GET`  | [`/v1/runs/{run_uid}`](/v1/api/runs/get)               | Fetch detailed information and status for a specific run using its unique identifier.                                |
| Get run status  | `GET`  | [`/v1/runs/{run_uid}/status`](/v1/api/runs/status)     | Lightweight status check for a run without fetching full details.                                                    |
| Get run inputs  | `GET`  | [`/v1/runs/{run_uid}/inputs`](/v1/api/runs/inputs)     | Retrieve all inputs for a run with their status and errors. Includes failed inputs that aren't available in outputs. |
| Get run outputs | `GET`  | [`/v1/runs/{run_uid}/outputs`](/v1/api/runs/outputs)   | Poll for run outputs without using callbacks. Supports cursor-based pagination via `X-Pagination-Next`.              |
| Resume a run    | `POST` | [`/v1/runs/{run_uid}/resume`](/v1/api/runs/manage)     | Resume a paused or stopped run.                                                                                      |
| Continue a run  | `POST` | [`/v1/runs/{run_uid}/continue`](/v1/api/runs/continue) | Continue an `incremental` sync run to fetch new data since the last retrieval.                                       |
| Cancel a run    | `POST` | [`/v1/runs/{run_uid}/cancel`](/v1/api/runs/cancel)     | Stop an async/schedule run. Already processed results remain available.                                              |

<Note>Refer to the [API Reference](/v1/api/runs/list) for request/response details and usage examples for each endpoint. </Note>

## Schedule Mode

When you run an action in `schedule` mode, a CRON-job is created that executes at defined intervals.

**Key characteristics:**

* **Scheduled execution** - runs at defined times (daily, weekly, custom CRON expressions)
* **Postponed execution** - can delay execution to a specific time
* **Automatic pagination** - same as Async mode
* **Callback-based** - results delivered via callbacks like Async mode
* **Recurring tasks** - ideal for regular data syncs and automated workflows
* **Automatic retries** - same retry behavior as [async mode](#async-mode)

<Info>
  Schedule mode is ideal for recurring tasks, regular data syncs, or when you want to automate actions at specific intervals
  without implementing your own scheduling system.

  Results are delivered progressively exactly like in Async mode, via callbacks on the URL set in the `callback.url` parameter.
</Info>

### Understanding CRON Expressions

A CRON expression is a string used to define the schedule for recurring jobs. It consists of five fields separated by spaces, representing:

1. Minute (0-59)
2. Hour (0-23)
3. Day of month (1-31)
4. Month (1-12)
5. Day of week (0-6, where 0 = Sunday)

**Examples:**

* `0 9 * * 1-5` — Every weekday (Monday to Friday) at 9:00 AM
* `30 2 * * *` — Every day at 2:30 AM
* `0 0 1 * *` — On the first day of every month at midnight
* `*/30 * * * *` — Every 30 minutes

You can use online tools like [crontab.guru](https://crontab.guru/) to build and test your CRON expressions.

### Available Endpoints for Schedules

You can manage and monitor your schedules using the following endpoints:

| Action             | Method | Endpoint                                                                 | Description                                                                                   |
| ------------------ | ------ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| List all schedules | `GET`  | [`/v1/schedules`](/v1/api/schedules/list)                                | Retrieve a list of all schedules, with filtering and pagination options.                      |
| Get a schedule     | `GET`  | [`/v1/schedules/{scheduled_run_uid}`](/v1/api/schedules/get)             | Fetch detailed information and status for a specific schedule using its unique identifier.    |
| Manage a schedule  | `POST` | [`/v1/schedules/{scheduled_run_uid}/{action}`](/v1/api/schedules/manage) | Update, pause, resume, or delete a schedule. Provide the schedule UID and the desired action. |

<Note>Refer to the [API Reference](/v1/api/schedules/list) for request/response details and usage examples for each endpoint. </Note>

## Incremental Sync Mode

Both **Async** and **Schedule** modes support an optional **incremental sync** mechanism. Instead of retrieving the full dataset on every run, you can set `sync_mode: "incremental"` inside the `parameters` object to fetch **only new data** since the last retrieval.

<Warning>
  Incremental sync is only available on [engagement-free actions](/v1/identities/engagement#included-actions) and requires an **Engagement Identity** (`type: "engagement"`). Standard identities cannot use this feature.
</Warning>

### How It Works

1. **First run (initial full sync):** When `sync_mode` is set to `incremental` and no previous data has been retrieved, the system performs a full sync limited by `max_results` (either the value you set or the action's default).
2. **Subsequent runs (incremental):** Once a first retrieval has been completed, the next runs automatically switch to incremental mode and return only newly available data.

<Warning>
  With each continue or scheduled iteration, the system fetches up to `max_results` new items.
  If more new items were created than the `max_results` limit between two iterations, items beyond that limit will be **lost** and cannot be recovered.

  This happens because data is always fetched **from the most recent to the oldest**.

  To minimize this risk, choose a `max_results` value and schedule frequency that match your expected data volume.
</Warning>

### Using Incremental Sync with Async Mode

In async mode, you trigger incremental updates manually by **continuing** a completed run:

```bash theme={null}
curl -X POST "https://api.edges.run/v1/runs/{run_uid}/continue" \
  -H "Accept: application/json" \
  -H "X-API-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json"
```

Each call to [`POST /v1/runs/{run_uid}/continue`](/v1/api/runs/continue) resets the run and fetches up to `max_results` new items.

### Example: Async Incremental Sync

```bash theme={null}
# 1. Launch an async run with incremental sync
curl -X POST "https://api.edges.run/v1/actions/linkedin-extract-connections/run/async" \
  -H "Accept: application/json" \
  -H "X-API-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{ "linkedin_url": "https://linkedin.com/in/johndoe" }],
    "callback": { "url": "https://yourdomain.com/callback" },
    "parameters": {
      "sync_mode": "incremental"
    }
  }'

# Returned body
{
  "run_id":xxxxxxxx,
  "run_uid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", (use this one)
...
  "action_name":"linkedin-extract-connections",
  "status":"SCHEDULED"
}

# 2. Once the run completes, continue it later to get new data
curl -X POST "https://api.edges.run/v1/runs/{run_uid}/continue" \
  -H "Accept: application/json" \
  -H "X-API-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json"
```

### Using Incremental Sync with Schedule Mode

In schedule mode, incremental sync is automatic: each scheduled iteration fetches only the new data since the last execution. No manual continue call is needed.

<Warning>
  In scheduled mode, if the previous run is still in progress when the next iteration is triggered, that iteration will be **skipped**. The following iteration will trigger the update instead.
</Warning>

### Example: Scheduled Incremental Sync

```bash theme={null}
# Schedule a recurring incremental sync every day at 9am
curl -X POST "https://api.edges.run/v1/actions/linkedin-extract-messages/run/schedule" \
  -H "Accept: application/json" \
  -H "X-API-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{ "conversation_id": "..." }],
    "callback": { "url": "https://yourdomain.com/callback" },
    "cron": "0 9 * * *",
    "timezone": "Europe/Paris",
    "parameters": {
      "sync_mode": "incremental"
    }
  }'
```

### Supported Actions

The following actions currently support incremental sync:

| Action                                                                                 | Description                     |
| -------------------------------------------------------------------------------------- | ------------------------------- |
| [`linkedin-extract-connections`](/v1/api/actions/linkedin-extract-connections)         | Extract LinkedIn connections    |
| [`linkedin-extract-messages`](/v1/api/actions/linkedin-extract-messages)               | Extract LinkedIn messages       |
| [`linkedin-extract-profile-viewers`](/v1/api/actions/linkedin-extract-profile-viewers) | Extract profile viewers         |
| [`linkedin-extract-page-followers`](/v1/api/actions/linkedin-extract-page-followers)   | Extract LinkedIn page followers |

### Continuable Run Statuses

A run can only be continued if its status is one of:

* `BLOCKED`
* `STOPPED`
* `FAILED`
* `PARTIAL_SUCCEEDED`
* `SUCCEEDED`

When continued, the run status and all related inputs are reset to `SCHEDULED`.

### Limitations

<Warning>
  With each continue or scheduled iteration, the system fetches up to `max_results` new items.
  If more new items were created than the `max_results` limit between two iterations, items beyond that limit will be **lost** and cannot be recovered.

  This happens because data is always fetched **from the most recent to the oldest**.

  To minimize this risk, choose a `max_results` value and schedule frequency that match your expected data volume.
</Warning>

## Quick Decision Guide

* **Live** ⚡️ - Need results immediately and can handle pagination manually
* **Async** 📦 - Running batch jobs with automated pagination and callback delivery
* **Schedule** 🕒 - Need recurring or postponed execution with the same benefits as Async
