Ingest and serve API
Send rows into a dataset, start a pipeline run with parameters and read its results, and read the latest rows of a dataset, all over plain HTTPS with a secret URL.
Three integrations give a workspace an HTTP surface without any sign-in dance. Each one is a single secret URL created in Jsonify.
| Integration | Direction | URL shape | Purpose |
|---|---|---|---|
| Receive JSON webhook | You send rows in | POST https://factory.jsonify.com/api/ingest/<id> |
Each request writes a new version of a dataset worksheet |
| Pipeline Trigger | You start a run | POST https://factory.jsonify.com/api/ingest/<id> |
Starts one pipeline, optionally with parameters, and gives you URLs to follow it |
| Publish Dataset API | You read rows out | GET https://factory.jsonify.com/api/serve/<id> |
Returns the latest version of a dataset worksheet as JSON |
<id> is the integration’s own identifier, shown on its detail page. A fourth integration, Fetch external JSON, goes the other way round: Jsonify fetches rows from a URL of yours. It is covered at the end.
The URL is the secret. There is no separate key, header or signature: anyone who has the URL can use it until the integration is deleted. Keep it out of shared documents and front-end pages, and if it leaks, delete the integration and create a new one; the old URL stops resolving the moment you delete it.
- Createpick the dataset or pipeline
- Copy the URLOverview tab
- Call itPOST or GET
- Follow upnew version, or run status and results
Receive rows over a webhook
Receive JSON webhook gives a dataset worksheet an address that other systems can post rows to: a form tool, an internal service, a partner’s export, a scheduled job.
Set it up
| Option | Values | Default | What it does |
|---|---|---|---|
| Label | Text | “Receive JSON webhook” | How the integration appears in pickers |
| Target dataset | A dataset, or + Create new dataset | Required | Rows posted to the URL land here |
| Target worksheet | A worksheet of that dataset | Required | Which worksheet receives them |
Press Connect; there is nothing to test. The integration is Live at once and its Overview tab shows the Ingest endpoint block with the URL, the dataset’s current version and when it last changed. The detail page’s Code tab holds a cURL sample and a second sample for your own program, each with a Copy button.
Send rows
Post one JSON object, or an array of objects. Each object is a row; its keys are column names.
curl -X POST https://factory.jsonify.com/api/ingest/int7f2c9a1b3 \
-H 'Content-Type: application/json' \
-d '[
{"product_url": "https://www.example-store.com/p/12345", "retailer": "Example Store", "price": 149.0, "currency": "GBP"},
{"product_url": "https://www.example-store.com/p/12399", "retailer": "Example Store", "price": 189.0, "currency": "GBP"}
]'
The answer confirms what was written:
{"ok": true, "integration": "int7f2c9a1b3", "dataset": "dsa41b7c9e2f0", "version": 18, "rows_ingested": 2}
Every successful request creates a new dataset version. If the dataset has key columns, incoming rows replace rows with the same key and new keys are added; if it has none, the rows are appended below the existing ones. Keys become column names, so keep them consistent between requests. Anything in the array that is not an object is dropped.
After the version is written, the dataset does everything it does after a pipeline publish: alert rules are evaluated, sinks deliver, dashboards recompute. The dataset page shows the version with the source Ingested.
Errors
| Answer | Meaning |
|---|---|
400 “response must be a JSON object or array of objects” |
The body was not JSON, or not an object or array |
400 “No rows to write” |
An empty array, or an array with no objects in it |
400 “Integration is not live” |
The integration is not Live. Open it and check its status |
400 “This integration is configured for pull mode” |
You posted to a Fetch external JSON integration, which is read by Jsonify, not written to |
404 “Integration not found” |
Wrong URL, or the integration was deleted |
404 “Target dataset not found” |
The dataset was deleted. Open the integration and pick another |
A partner emails a nightly CSV, and a small job on your side converts it to JSON and posts it to the ingest URL. The dataset Partner stock gains a version each night, an alert rule watches for products that disappeared, and a sheet mirrors it for the buying team. No pipeline involved.
Start a run from a webhook
Pipeline Trigger gives one pipeline an address that starts a run. Use it from a scheduler you already have, a deploy step, a Zapier zap, or anything that can send a request.
Set it up
| Option | Values | Default | What it does |
|---|---|---|---|
| Label | Text | “Run pipeline on POST” | How the integration appears in pickers |
| Pipeline to run | A pipeline with an active version | Required | Which pipeline each request starts |
Press Connect. The Overview tab shows the Trigger endpoint block with the URL, followed by the pipeline’s parameters: each one’s name, type, default and description. Those are the fields you may send in the body. The pipeline’s own Run dialog also shows a banner, “Pipeline Trigger attached”, linking back to the integration, so anyone running it by hand knows it can also be started from outside.
Start a run
An empty body starts the pipeline with its defaults, exactly as pressing Run would:
curl -X POST https://factory.jsonify.com/api/ingest/intd3e8f1a2b4
To pass parameters, send them as top-level JSON fields. Two optional run controls sit alongside them: row_limit (the row cap for this run; 0 means a full run) and pipeline_version (run a specific version rather than the active one).
curl -X POST https://factory.jsonify.com/api/ingest/intd3e8f1a2b4 \
-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 parameter both ways; the request is refused. Parameters are validated exactly as in the Run dialog; a wrong type or an unknown name is a 400 with the reason.
The answer arrives as soon as the run is queued:
{
"ok": true,
"run_id": "mrun00000361",
"pipeline": "marketpipe03",
"status": "queued",
"status_url": "https://factory.jsonify.com/api/ingest/intd3e8f1a2b4/runs/mrun00000361"
}
Only one run per pipeline can be active. While one is queued or running, another request gets 409 with "error": "Pipeline already has an active run" plus the active run_id and its status; nothing is queued behind it. Wait for the status URL to report a finished run, then post again.
Runs started this way show the trigger webhook on the runs list. If one fails persistently, it is eligible for automatic repair in the same way as a scheduled run.
Check the status
status_url is specific to the 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/intd3e8f1a2b4/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/intd3e8f1a2b4/runs/mrun00000361/results/marketdata02?table=main"
}
],
"results_truncated": false
}
results is empty until the run completes. A failed run adds an error field. A run that published to more than 100 datasets lists the first 100 and sets results_truncated to true; open the run in Jsonify for the rest.
Read the results
Each result_url returns the exact dataset version the run produced, a page at a time. limit is at most 100 rows; follow next_url while has_more is true.
curl 'https://factory.jsonify.com/api/ingest/intd3e8f1a2b4/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/intd3e8f1a2b4/runs/mrun00000361/results/marketdata02?table=main&offset=100&limit=100"
}
Asking for results before the run has completed answers 409 “Trigger run is not completed”. Because the version is pinned, you can page through it at leisure; a later run does not change what this URL returns.
| Answer | Meaning |
|---|---|
400 “Invalid run config:” |
A parameter failed validation, or was supplied both directly and inside params |
409 “Pipeline already has an active run” |
Wait for the current run, then post again |
409 “Pipeline has no active revision” |
The pipeline has never been activated; finish its first build in Jsonify |
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 |
A deployment step posts to the trigger after each catalogue import, waits on the status URL, then pages the result into the search index. The run’s row cap, parameters and version are all in the request, so the step needs nothing from the Jsonify UI.
Serve a dataset over HTTP
Publish Dataset API gives a dataset worksheet an address that returns its latest rows. Your app, your BI tool’s web connector, or a scheduled loader fetches it whenever it likes.
Set it up
| Option | Values | Default | What it does |
|---|---|---|---|
| Label | Text | “Publish dataset API” | How the integration appears in pickers |
| Source dataset | A dataset | Required | The dataset whose rows are returned |
| Source worksheet | A worksheet of that dataset | Required | Which worksheet is returned |
Press Connect. The Overview tab shows the Serve endpoint block with the URL and the dataset’s current version.
Read rows
curl https://factory.jsonify.com/api/serve/int9c4e2b7d1a
{
"integration": "int9c4e2b7d1a",
"dataset": "marketdata01",
"dataset_name": "Live product offers",
"table": "main",
"version": 42,
"updated_at": 1789452498,
"row_count": 3120,
"rows": [
{"product_url": "https://www.example-store.com/p/12345", "retailer": "Example Store", "title": "Espresso machine 15 bar", "price": 149.0, "currency": "GBP", "in_stock": true},
{"...": "..."}
]
}
The endpoint always returns the active version of the worksheet, in full, as JSON. There are no query parameters: no paging, no filters, no other formats. A dataset that has not published yet answers with "version": null and an empty rows array. Compare version between fetches to know whether anything changed.
For a filtered or paged read, or CSV, use the command line or an MCP assistant instead; both sign in with a credential rather than a URL.
Serve URLs are read by whoever holds them. Do not embed one in a public web page or a shared notebook. For sharing a dataset with people, use a share link, which can be turned off and shows a proper page.
Fetch rows from your own endpoint
Fetch external JSON is the mirror of the ingest webhook: instead of you posting rows, Jsonify fetches them from a URL you own and writes them into a dataset. Use it for a read-only endpoint that already exists.
| Option | Values | Default | What it does |
|---|---|---|---|
| Label | Text | “Fetch external JSON” | How the integration appears in pickers |
| Endpoint URL | An http:// or https:// URL |
Required | Where rows are fetched from. Must return JSON |
| Target dataset | A dataset, or + Create new dataset | Required | Where the rows go |
| Target worksheet | A worksheet of that dataset | Required | Which worksheet receives them |
| Auth header (optional) | A full header line, for example Authorization: Bearer … |
Empty | Sent on every fetch. Leave blank for an open endpoint |
Connect fetches the endpoint once, without the auth header, and checks the answer is JSON. If your endpoint refuses unauthenticated requests, the check fails; make it answer with an empty JSON array for unauthenticated calls, or allow Jsonify’s check through some other way, then save.
The endpoint may return an array of objects, or an object whose data, results, items or rows key holds the array.
Like a pulled sheet, this source has no timer of its own: Jsonify fetches it before every run of a pipeline that uses the dataset as an input, and when you sync the dataset by hand. Each fetch that returns rows writes a new version, merged by key when the dataset has key columns. See Pull rows from a sheet for the same behaviour explained for sheets.
Keeping URLs private
What’s next
Row caps, versions and parameters, as the Run dialog offers them.
Dataset versionsWhy every ingest and every run is a new version, and how to look back.
Connect your agent (MCP)Read datasets and start runs from ChatGPT, Claude or another assistant.
Command lineFiltered reads, exports and runs from a terminal or a job.