---
title: "Start runs from your systems"
description: "A pipeline trigger is a secret URL that starts a run with your inputs and returns the exact rows that run produced. Up to four runs at once, a test dialog, and ready-made code."
url: "https://www.jsonify.com/docs/integrations/triggers"
---

[Documentation](https://www.jsonify.com/docs/) Filter pages

## Getting started

*   [What is Jsonify?](https://www.jsonify.com/docs/)
*   [How pipelines work](https://www.jsonify.com/docs/how-jsonify-works)
*   [Your first pipeline](https://www.jsonify.com/docs/getting-started/first-pipeline)
*   [Workspaces](https://www.jsonify.com/docs/getting-started/workspaces)
*   [Glossary](https://www.jsonify.com/docs/getting-started/glossary)

## Tutorials

*   [Track the Hacker News front page](https://www.jsonify.com/docs/tutorials/hacker-news)
*   [Monitor prices across retailers](https://www.jsonify.com/docs/tutorials/price-monitoring)
*   [Get a Slack alert when a price drops](https://www.jsonify.com/docs/tutorials/slack-price-alert)
*   [Use a dataset from your agent](https://www.jsonify.com/docs/tutorials/agent-query)

## Jason

*   [Chatting with Jason](https://www.jsonify.com/docs/jason/chat)
*   [Changing a pipeline](https://www.jsonify.com/docs/jason/changing-a-pipeline)
*   [Agents, workers and repairs](https://www.jsonify.com/docs/data-agents)

## Pipelines

*   [The pipeline page](https://www.jsonify.com/docs/pipelines/overview)
*   [Statement of work and steps](https://www.jsonify.com/docs/pipelines/statement-of-work)
*   [Inputs and parameters](https://www.jsonify.com/docs/pipelines/inputs)
*   [Pipeline integrations](https://www.jsonify.com/docs/pipelines/integrations)
*   [Settings reference](https://www.jsonify.com/docs/pipelines/settings)
*   [Versions and changelog](https://www.jsonify.com/docs/pipelines/versions)

## Runs

*   [Running a pipeline](https://www.jsonify.com/docs/runs/running)
*   [Scheduling](https://www.jsonify.com/docs/runs/scheduling)
*   [Run detail](https://www.jsonify.com/docs/runs/run-detail)
*   [Run lifecycle](https://www.jsonify.com/docs/runs/lifecycle)
*   [Verification and repair](https://www.jsonify.com/docs/runs/verification-and-repair)

## Datasets

*   [Dataset pages](https://www.jsonify.com/docs/datasets/overview)
*   [Versions](https://www.jsonify.com/docs/datasets/versions)
*   [Query and export](https://www.jsonify.com/docs/datasets/query-and-export)
*   [Row detail and provenance](https://www.jsonify.com/docs/datasets/provenance)

## Analytics

*   [Dashboards](https://www.jsonify.com/docs/analytics/dashboards)
*   [Editing dashboards](https://www.jsonify.com/docs/analytics/editing-dashboards)

## Alerts

*   [Alert rules](https://www.jsonify.com/docs/alerts/rules)
*   [Notifications](https://www.jsonify.com/docs/alerts/history)

## Integrations

*   [Integrations overview](https://www.jsonify.com/docs/integrations/overview)
*   [Sheets, warehouses and webhooks](https://www.jsonify.com/docs/integrations/destinations)
*   [Warehouses and S3](https://www.jsonify.com/docs/integrations/warehouses)
*   [Slack and email](https://www.jsonify.com/docs/integrations/slack-and-email)
*   [Start runs from your systems](https://www.jsonify.com/docs/integrations/triggers)
*   [Ingest and serve API](https://www.jsonify.com/docs/integrations/api)
*   [Connect your agent (MCP)](https://www.jsonify.com/docs/integrations/mcp)
*   [Command line](https://www.jsonify.com/docs/integrations/cli)

## Workspace and account

*   [Workspace settings](https://www.jsonify.com/docs/workspace/settings)
*   [Team](https://www.jsonify.com/docs/workspace/team)
*   [Sharing a workspace](https://www.jsonify.com/docs/workspace/sharing)
*   [The sidebar](https://www.jsonify.com/docs/workspace/sidebar)
*   [Usage](https://www.jsonify.com/docs/workspace/usage)
*   [Billing](https://www.jsonify.com/docs/workspace/billing)
*   [Data retention](https://www.jsonify.com/docs/workspace/data-retention)

## Plans

*   [Radar or Benchmark? Enterprise](https://www.jsonify.com/docs/radar-vs-benchmark)
*   [Row pricing and managed service](https://www.jsonify.com/docs/pricing-model)

## More

*   [Connect your agent](https://www.jsonify.com/connect)
*   [Pricing](https://www.jsonify.com/pricing)
*   [Contact support](https://www.jsonify.com/contact)

[Docs](https://www.jsonify.com/docs/)/Integrations

# Start runs from your systems

A pipeline trigger is a secret URL that starts a run with your inputs and returns the exact rows that run produced. Up to four runs at once, a test dialog, and ready-made code.

A **pipeline trigger** gives one pipeline a secret URL. POST JSON to it and a run starts with the inputs you sent. The answer carries a status URL for that run alone, and once the run completes, result URLs that return exactly the rows it produced. Use it from a scheduler you already have, a deploy step, a Zapier zap, or your own application.

Start

`POST /api/ingest/<trigger>`, answers 202

Follow

`status_url` for that run

Read

`result_url`, 100 rows a page

At once

Up to four runs per pipeline; 429 beyond that

1.  **POST inputs**the pipeline's inputs as JSON
2.  **202 + status\_url**one run per request
3.  **Poll status**until completed
4.  **Page the results**the exact version this run wrote

Every request gets its own run and its own results.

> The URL is the secret. There is no separate key, header or signature. Anyone who has the trigger URL, or a status or result URL built from it, can use it until the trigger is deleted. Keep it server-side, and if it leaks, delete the trigger and create a new one.

## Create a trigger

1.  **Ask for one.** Open **Integrations → Inputs → Connect Input**, choose **Webhooks**, and pick the suggestion “Start a pipeline run when my system calls a URL”, or say which pipeline. From a pipeline’s **Integrations** tab, **Add input** does the same with the pipeline already named.
2.  **Open it.** Jason creates the trigger and links to it. The card and the page header show the pipeline’s icon.
3.  **Copy the URL.** It is on the **Settings** tab.

The pipeline’s own **Run** dialog shows a banner, “Pipeline Trigger attached”, with a link to the trigger (“2 triggers attached” when there are several), so anyone running it by hand knows it can also be started from outside.

## Trigger settings

![A Pipeline Trigger's Settings tab with the Trigger endpoint heading, the Pipeline to run selector, the URL with Copy, and the list of pipeline inputs](/assets/images/product/integrations-trigger.png)

The Settings tab of a trigger. The inputs listed are the fields you may send.

The Settings tab reads, top to bottom:

1.  **Trigger endpoint.** “POST JSON to start a run. Up to four can run at once (429 if full). The response includes `run_id` and a secret `status_url` for exact results.”
2.  **Pipeline to run.** A pipeline with an active version. Required. A workspace with none shows “No pipelines with an active revision in this workspace yet.”
3.  **URL.** The trigger URL, with **Copy**.
4.  **Pipeline inputs.** One row per input the pipeline accepts: its id, its type (`string`, `int`, `list[url]` and so on), “Default:” with its default value when it has one, and its description. These are the fields you may send in the body.
5.  **Save.** Stores a change of pipeline.

The tabs after Settings are **Code** (see [Code samples](https://www.jsonify.com/docs/integrations/triggers#code-samples)), **Activity** (every request, accepted or refused, with the run it started) and **Delete**. Deleting the trigger stops its URL, and every status and result URL built from it, immediately.

## Start a run

An empty body starts the pipeline with its defaults and its default row cap, exactly as pressing **Run** would:

```
curl -X POST https://factory.jsonify.com/api/ingest/marketint009
```

To pass inputs, send them as top-level JSON fields. Two optional run controls sit alongside them:

| Field | Values | Default | What it does |
| --- | --- | --- | --- |
| each input id | As its type | The input’s default | One field per pipeline input, as listed on the Settings tab |
| row_limit | 0 or a positive number | The pipeline’s default row cap | The row cap for this run. 0 runs all rows |
| pipeline_version | A version number | The active version | Run a specific version instead |

```
curl -X POST https://factory.jsonify.com/api/ingest/marketint009 \
  -H 'Content-Type: application/json' \
  -d '{"region": "uk", "include_out_of_stock": false, "row_limit": 0}'
```

The nested form `{"params": {"region": "uk"}, "row_limit": 0}` is also accepted. Do not send the same input both ways; the request is refused. Inputs are checked as in the [Run dialog](https://www.jsonify.com/docs/runs/running): a wrong type or an unknown name is a `400` with the reason.

The answer is HTTP `202 Accepted` as soon as the run is queued:

```
{
  "ok": true,
  "run_id": "mrun00000361",
  "pipeline": "marketpipe03",
  "status": "queued",
  "status_url": "https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361"
}
```

Runs started this way show the trigger **webhook** on the [runs list](https://www.jsonify.com/docs/runs/run-detail), and the run’s **Integrations** tab names the trigger under **In**. They count rows like any other run, and a failing one is eligible for [automatic repair](https://www.jsonify.com/docs/runs/verification-and-repair) in the same way as a scheduled run.

## Up to four runs at once

Each pipeline can have up to four runs queued or running at the same time, whether they came from the trigger, the **Run** button or another tool. Every accepted request gets its own run, even when two requests send identical inputs.

A fifth request, while four are still outstanding, is refused with HTTP `429`:

```
{"error": "Pipeline has 4 outstanding runs; retry after one finishes"}
```

Nothing is queued behind it. Wait a moment and send it again; the [code samples](https://www.jsonify.com/docs/integrations/triggers#code-samples) do this for you. The same limit applies to the **Run** button in Jsonify, and the pipeline page shows one “Run 3f9a2c is active” banner per run in flight. A scheduled run is skipped while its pipeline has a run outstanding.

Runs of the same pipeline each publish their own [dataset version](https://www.jsonify.com/docs/datasets/versions). The one that finishes publishing last becomes the dataset’s active version. That is why you should always read results through the URLs a request returned, never through the dataset’s latest version: they point at the exact version your run wrote.

## Check the status

`status_url` belongs to one run and to the trigger that started it. Fetch it until `status` is `completed`, `failed` or `cancelled`; every couple of seconds is plenty.

```
curl https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361
```

```
{
  "ok": true,
  "run_id": "mrun00000361",
  "pipeline": "marketpipe03",
  "status": "completed",
  "created_at": 1789452131,
  "finished_at": 1789452498,
  "results": [
    {
      "dataset": "marketdata02",
      "dataset_name": "Price observations",
      "version": 61,
      "table": "main",
      "row_count": 1184,
      "result_url": "https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361/results/marketdata02?table=main"
    }
  ],
  "results_truncated": false
}
```

*   **`results`** is empty until the run completes. There is one entry per worksheet of each dataset the run published.
*   **`error`** is added when the run failed.
*   **`results_truncated`** is `true` when the run produced more than 100 results; the first 100 are listed. Open the run in Jsonify for the rest.

### Output results

A pipeline that publishes no dataset, and only produces run outputs, lists those instead. Each entry has `output`, `description`, `row_count` and a `result_url` ending in `/results/output-<id>`:

```
{
  "output": "Weekly price summary",
  "description": "One row per retailer",
  "row_count": 24,
  "result_url": "https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361/results/output-8812"
}
```

## Read the results

Each `result_url` returns a page of the exact version, or output, that the run produced. Pages hold up to 100 rows; add `offset` and `limit` to choose, and follow `next_url` while `has_more` is `true`.

```
curl 'https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361/results/marketdata02?table=main&offset=0&limit=100'
```

```
{
  "ok": true,
  "run_id": "mrun00000361",
  "pipeline": "marketpipe03",
  "dataset": "marketdata02",
  "dataset_name": "Price observations",
  "version": 61,
  "table": "main",
  "row_count": 1184,
  "offset": 0,
  "limit": 100,
  "rows": [ { "...": "..." } ],
  "has_more": true,
  "next_url": "https://factory.jsonify.com/api/ingest/marketint009/runs/mrun00000361/results/marketdata02?table=main&offset=100&limit=100"
}
```

An output result has the same shape, with `output` and `description` in place of `dataset`, `dataset_name`, `version` and `table`. Because the version is pinned, you can page through it at leisure; a later run does not change what this URL returns.

## Errors

| Answer | Meaning |
| --- | --- |
| 400 “Invalid run config:” | An input failed validation, was supplied both directly and inside params, or the body was not a JSON object |
| 400 “Integration is not live” | The trigger is not Live. Open it and check its status |
| 404 “Integration not found” | Wrong URL, or the trigger was deleted |
| 404 “Configured pipeline not found” | The pipeline was deleted. Open the trigger and pick another |
| 409 “Pipeline has no active revision” | The pipeline has never been activated; finish its first build in Jsonify |
| 429 “Pipeline has 4 outstanding runs; retry after one finishes” | Four runs are already queued or running. Retry shortly |
| 404 “Trigger run not found” | The run was not started by this trigger, or the identifier is wrong |
| 409 “Trigger run is not completed” | Results were requested before the run finished |
| 400 “offset and limit must be integers” | A paging parameter was not a whole number |

Every refused request is recorded on the trigger’s **Activity** tab as “Rejected a request”, with the reason.

## Test a trigger from Jsonify

**Test**, in the trigger’s page header, opens the **Test API request** dialog. It sends real requests from your browser, so a test starts a real run that counts rows.

![The Test API request dialog on its Start run tab with the pipeline's input fields, the Advanced section open showing Row limit and Pipeline version, and the request body preview](/assets/images/product/integrations-trigger-test.png)

Start run. The body preview updates as you fill in the inputs.

The dialog has two tabs.

**Start run:**

*   **The pipeline’s inputs.** The same fields as the Run dialog, filled with each input’s default. Required inputs must have a value; “{input} is required.” otherwise.
*   **Advanced.** Collapsed. **Row limit**, default `0`, “0 runs all rows”. **Pipeline version**, blank, placeholder “Latest”.
*   **Request body.** A read-only preview of the JSON that will be sent.
*   **Send request.** Posts it. The **Response** box shows the HTTP status and the answer. On success the dialog moves to **Get result** with the new run’s ID filled in.

**Get result:**

*   **Run ID.** The run to look up, with the status URL shown below it.
*   **Fetch result.** Reads the status. For a run that has not completed, it shows the status as it is. For a completed run it also follows the result URLs, up to 10 pages in all, and shows the rows; `preview_truncated` is `true` when there was more.

**Close** leaves the dialog. Every request also appears on the **Activity** tab.

## Code samples

The **Code** tab holds three samples built from the trigger’s real URL and the pipeline’s current inputs. Each is a collapsible card with **Copy**; **Python** comes first and starts open, followed by **cURL** and **JavaScript (Node.js)**.

![The Code tab of a trigger with the Python sample expanded and collapsed cURL and JavaScript cards below it](/assets/images/product/integrations-trigger-code.png)

The Code tab. Each sample starts a run, waits for it and reads every result page.

Every sample does the whole job:

1.  **Sets your inputs** as constants at the top, with each input’s default and, in Python, the other allowed values as a comment. `ROW_LIMIT = 0` runs all rows; `PIPELINE_VERSION` is empty for the latest.
2.  **Starts the run**, and retries every two seconds while the pipeline is at its four-run limit (`429`).
3.  **Polls the status URL** every two seconds until the run completes, fails or is cancelled.
4.  **Reads every result page**, following `next_url`, and prints the rows.

The Python and JavaScript samples stop after five minutes, and raise an error if the run failed or produced more than 100 results. The cURL sample needs `jq`.

The essential Python flow is:

```
started = requests.post(TRIGGER_URL, json={**inputs, "row_limit": 0}, timeout=30)
# 429 means four runs are outstanding: wait and post again.
status = requests.get(started.json()["status_url"], timeout=30).json()
# ...poll until status["status"] == "completed", then page each result_url.
```

> A deployment step posts to the trigger after each catalogue import, waits on the status URL, then pages the result into the search index. Two imports that land at once each get their own run and their own rows. The row cap, inputs and version are all in the request, so the step needs nothing from the Jsonify UI.

## What’s next

[**Running a pipeline**

Row caps, versions and inputs, as the Run dialog offers them.

](https://www.jsonify.com/docs/runs/running)[**Ingest and serve API**

Push rows into a dataset, or read its latest rows.

](https://www.jsonify.com/docs/integrations/api)[**Inputs and parameters**

Where a pipeline's inputs and their defaults come from.

](https://www.jsonify.com/docs/pipelines/inputs)[**Command line**

Start and follow runs with a key instead of a URL.

](https://www.jsonify.com/docs/integrations/cli)

[PreviousSlack and email](https://www.jsonify.com/docs/integrations/slack-and-email) [NextIngest and serve API](https://www.jsonify.com/docs/integrations/api)

Something missing or wrong? [Tell us](https://www.jsonify.com/contact).[Build it →](https://factory.jsonify.com/setup)

## On this page
