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

# Use a webhook payload in a Bolt run

> Start a Bolt schedule from another tool with an incoming webhook, and read the JSON body of the request in a Python script or a dbt™ model in that run.

An [incoming webhook](https://docs.paradime.io/products/bolt/external-triggers/incoming-webhooks.md) starts a Bolt schedule when another tool sends a `POST` request to its URL. When the request has a JSON body, every command in the run can read it from an environment variable. Use this when the run must know which event started it, for example which source finished loading, or up to which time it loaded data.

In this guide, an ingestion job calls the webhook when it finishes a load. The body tells the run which source loaded. A dbt™ macro logs the event, and a dbt™ model reads the load time from the body. You can also read the body in a Python script.

> [!NOTE]
>
> **Prerequisites**
>
> * A [Bolt schedule](https://docs.paradime.io/guides/paradime-101/running-dbt-in-production-with-bolt/creating-bolt-schedules.md) you can edit.
> * The Admin, Workspace Admin, or Developer role, to create incoming webhooks.
> * A tool that can send a `POST` request with a custom header and a JSON body, such as a CI job, Zapier, Jira Automation, or `curl`.
>
> Estimated time: 15 minutes.
>
> To start a DinoAI agent from a webhook, see [Run an agent from a webhook](https://docs.paradime.io/guides/programmable-agents/run-an-agent-from-a-webhook.md).

## How the payload reaches the run

Every run that an incoming webhook starts gets two environment variables:

| Variable | Value |
| - | - |
| `PARADIME_WEBHOOK_EVENT_ID` | The ID of the request. Set on every run that an incoming webhook starts. |
| `PARADIME_WEBHOOK_PAYLOAD` | The request body as compact JSON, such as `{"source":"orders","loaded_until":"2026-10-27T06:00:00Z"}`. |

`PARADIME_WEBHOOK_PAYLOAD` is set only when the body is a JSON object or array of 64 KB or less. It isn't set when:

* The request is a **Send test request** from Bolt, which has no body.
* The request has no body, or a body over 1 MB.
* The body isn't a JSON object or array, such as plain text or a single number.
* The body is over 64 KB.

The run still starts in each case. So always give a default when you read the variable.

## Steps

**1. Read the payload in your commands**

You don't need Python. dbt™ reads the payload with `env_var` and `fromjson`.

In a model, read the load time from the body:

```sql title="models/staging/stg_orders_latest_load.sql" theme={"system"}
{% set payload = fromjson(env_var('PARADIME_WEBHOOK_PAYLOAD', '{}')) %}
{% set loaded_until = payload.get('loaded_until', '1970-01-01T00:00:00Z') %}

select *
from {{ source('shop', 'orders') }}
where loaded_at <= '{{ loaded_until }}'
```

To write the event to the run's logs, add a macro and call it with `dbt run-operation`:

```sql title="macros/log_webhook_event.sql" theme={"system"}
{% macro log_webhook_event() %}
  {% set payload = fromjson(env_var('PARADIME_WEBHOOK_PAYLOAD', '{}')) %}
  {% if payload.get('source') %}
    {% do log('Event ' ~ env_var('PARADIME_WEBHOOK_EVENT_ID', '') ~ ': ' ~ payload['source'] ~ ' loaded until ' ~ payload.get('loaded_until'), info=True) %}
  {% else %}
    {% do log('No usable webhook payload, nothing to do', info=True) %}
  {% endif %}
{% endmacro %}
```

The schedule's commands, in order:

```bash theme={"system"}
dbt run-operation log_webhook_event
dbt build --select stg_orders_latest_load+
```

> [!WARNING]
>
> The body comes from outside Paradime. Check each value before you put it in SQL or in a shell command, for example that `loaded_until` is a timestamp. Otherwise, whoever can call the webhook can change the SQL that runs.

> **Pass the payload as dbt vars**
>
> In a `dbt` command, Bolt replaces `$PARADIME_WEBHOOK_PAYLOAD` with the body, as one argument. See [Environment variables in commands](https://docs.paradime.io/products/bolt/creating-schedules/environment-variables-in-commands.md). When the body is a JSON object, pass it as dbt vars:
>
> ```bash theme={"system"}
> dbt build --select stg_orders_latest_load+ --vars $PARADIME_WEBHOOK_PAYLOAD
> ```
>
> Models then read `{{ var('loaded_until') }}`. Use this only when every request has a JSON object body. When `PARADIME_WEBHOOK_PAYLOAD` isn't set, such as on a test request, Bolt doesn't replace the placeholder, and the command fails. The `env_var` approach above works in both cases.

> **Read the payload in Python**
>
> In a [Python script](https://docs.paradime.io/products/bolt/creating-schedules/command-settings/python-scripts.md), parse the variable with `json`:
>
> ```python title="scripts/log_webhook_event.py" theme={"system"}
> import json
> import os
>
> event_id = os.environ.get("PARADIME_WEBHOOK_EVENT_ID")
> payload = json.loads(os.environ.get("PARADIME_WEBHOOK_PAYLOAD", "{}"))
>
> source = payload.get("source")
> if source is None:
>     print(f"No usable payload for event {event_id}, nothing to do")
> else:
>     print(f"Event {event_id}: {source} loaded until {payload.get('loaded_until')}")
> ```
>
> Add `python scripts/log_webhook_event.py` as a command in place of `dbt run-operation log_webhook_event`.

**2. Create the Trigger URL**

1. In the schedule's edit form, under **Trigger type**, select **Incoming webhook**.
2. In the **Trigger URLs** card, click **New Trigger URL**, enter a **Name** such as `Orders ingestion`, and click **Create**.
3. Copy the **URL** and the **Authorization header**, then click **Done**. The secret shows only once.
4. Click **Deploy**.

**3. Send the event from the other tool**

At the end of the ingestion job, send a `POST` request with the `Authorization` header and a JSON body:

```bash theme={"system"}
curl -X POST "<your Trigger URL>" \
  -H "Authorization: Bearer <your secret>" \
  -H "Content-Type: application/json" \
  -d '{"source": "orders", "loaded_until": "2026-10-27T06:00:00Z"}'
```

The response has `"accepted": true` and a `run_id` when a run started. Store the secret in the tool's secret store, not in the job's code.

> [!TIP]
>
> Open the run in Bolt. It says **Triggered by an incoming webhook**. The logs of the `dbt run-operation` command show `Event <id>: orders loaded until 2026-10-27T06:00:00Z`.

## Define it as code

If you [manage schedules as code](https://docs.paradime.io/guides/orchestrate-data-pipelines/manage-schedules-as-code.md), declare the trigger with `trigger_on_webhook: true`:

```yaml theme={"system"}
schedules:
  - name: orders on load
    slug: orders-on-load-d4e5f6
    owner_email: data-team@acme.io
    environment: production
    git_branch: main
    schedule: "OFF"
    trigger_on_webhook: true
    commands:
      - dbt run-operation log_webhook_event
      - dbt build --select stg_orders_latest_load+
```

The file doesn't create the Trigger URL. After you deploy, open the schedule in Bolt and create the URL in the **Trigger URLs** card.

## Things to know

* **Requests aren't queued.** A request that arrives while the schedule is running gets `"skipped": "already_running"`, and no run starts for it. If every event must start a run, make the sender retry on that response, or send one request after a batch of loads.
* **Retries keep the payload.** A [retry](https://docs.paradime.io/products/bolt/managing-schedules/retrying-a-run.md) of the run gets the body of the original request.
* **One schedule, many senders.** Give each sender its own Trigger URL, and put a field such as `source` in the body, so a command can tell the senders apart.
* **See every request.** Go to **Bolt** > **Webhooks** > **Incoming** to see the requests each Trigger URL received and the run each one started.

## Next steps

> **[Incoming webhooks reference](https://docs.paradime.io/products/bolt/external-triggers/incoming-webhooks.md)**
>
> Responses, deliveries, rotation, and limits.

> **[Run an agent from a webhook](https://docs.paradime.io/guides/programmable-agents/run-an-agent-from-a-webhook.md)**
>
> Give the payload to a DinoAI agent.


## Related topics

- [Incoming webhooks](https://docs.paradime.io/products/bolt/external-triggers/incoming-webhooks.md)
- [Run an agent from a webhook](https://docs.paradime.io/guides/programmable-agents/run-an-agent-from-a-webhook.md)
- [Use the incoming webhook's request body in your Bolt run](https://docs.paradime.io/changelog/2026-10-20/webhook-payload.md)
- [Payload Transformations](https://docs.paradime.io/developers/webhooks/payload-transformations.md)
- [bolt.run.started](https://docs.paradime.io/developers/webhooks/bolt-run-started.md)


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