Skip to main content

Pipeline API — one call, many outputs

One request takes a recording (or any supported source file), adds it to a project's sources, transcribes it, and runs several templates on it in parallel. When every output is ready, the platform POSTs the download links to your callback_url. You can also poll for them.

POST /api/v1/notebooks/{notebook_id}/pipeline {file | file_url, template_ids, callback_url}
→ 202 {run_id, jobs[], credits_required}
→ … upload → transcribe → file appears in the project's Sources …
→ … every template runs on the FULL transcript (chained ones on their parent's output) …
→ POST callback_url {status: "done", jobs: [{template_name, filename, download_url}, …]}
Calling from Zapier, Make or n8n? Use research-api.onfire.so

research.onfire.so sits behind Cloudflare Bot Fight Mode, which challenges automation platforms that call from cloud data centres (Zapier's Code steps get a 403 that never reaches the API). The same API is served for machines at https://research-api.onfire.so/api/v1/…, covering both endpoints on this page. That host serves only /api/v1/; authentication and rate limits are identical.

It is the endpoint to call from Zapier, Make or n8n: it accepts an X-API-Key, so there is no login step and no token expiry. It also takes a form, multipart or JSON body.


POST /api/v1/notebooks/{notebook_id}/pipeline​

  • Auth: X-API-Key: rsk_… (create one with POST /api/api-keys). A JWT also works.
  • Permission: member or higher on the notebook.
  • Status: 202 Accepted.
  • Rate limit: 10 runs/hour per caller.

Fields​

Send them as multipart/form-data (required when uploading the file itself), application/x-www-form-urlencoded, or application/json.

FieldRequiredNotes
fileone ofThe file itself (multipart). Max ~100 MB through Cloudflare.
file_urlone ofA public http(s) URL the platform downloads, e.g. a Dropbox or Google Drive direct link. Up to 300 MB. Internal and private addresses are refused.
filenamenoOverrides the name taken from the upload or the URL. Set it when the URL has no file extension.
template_idsyesThe templates to run, up to 10. Use a list, or a comma-separated string. A custom template's id can be sent bare (9261e63e-…) or prefixed (usr:9261e63e-…). System templates use their id (email_html). Template IDs are shown in the template editor.
inputs_fromnoChains templates: child=parent, comma-separated, or a JSON object {"child": "parent"}. The child runs after the parent and receives the parent's text output as additional context.
topicnoDefaults to the filename with _ turned into spaces, e.g. SIHCX2025 1015 Playbook General Counsel Expectations Review. Templates that read the audience from the first word of the input see it first.
extra_contextnoFree text appended to every template's prompt.
modelnoA key from GET /api/notebooks/{id}/generate/models. Defaults to Claude Opus.
callback_urlnoA public URL that receives the final payload as a JSON POST. Zapier: use the URL of a Catch Hook trigger.

What each template sees​

  • The full transcript of this file. Templates do not get a search over the whole project. Older sources in the project do not leak into an output about this recording.
  • The project's instructions, brand kit and privacy settings, exactly as in normal generation.
  • Custom templates whose prompts were written for another tool, with no {{retrieved_context}} placeholder (the whole brief in the system prompt, for example), still receive the topic, the transcript and the JSON shape their renderer needs.

Response — 202 Accepted​

{
"run_id": "a82cc7cd-ffbd-44f0-84d7-0158926d59cf",
"status": "queued",
"notebook_id": "cda482a4-f401-4059-9160-dbd7efe05e04",
"file": {"id": "a82cc7cd-…", "filename": "SIHCX2025_1015_Playbook_General Counsel Expectations Review.mp3", "status": "ingesting"},
"topic": "SIHCX2025 1015 Playbook General Counsel Expectations Review",
"credits_required": 315,
"status_url": "/api/v1/pipeline-runs/a82cc7cd-ffbd-44f0-84d7-0158926d59cf",
"jobs": [
{"job_id": "f4240533-…", "template_id": "usr:9261e63e-…", "template_name": "Mailchimp Post Exchange Email", "credits": 45, "input_from": null, "status": "queued"},
{"job_id": "58c8fa58-…", "template_id": "usr:fd90aacd-…", "template_name": "Mighty Networks Article", "credits": 45, "input_from": "usr:9261e63e-…", "status": "queued"}
]
}

run_id is also the id of the new source file in the project.

Errors​

StatusWhen
401Missing or invalid API key
402The balance does not cover every template. Nothing is stored.
404Notebook not found / not yours, or an unknown template_id
413Upload over 300 MB
422Neither or both of file/file_url; unsupported file type; no template_ids; more than 10; inputs_from naming a template not in the list, or forming a cycle; a refused URL
429More than 10 runs in the hour

GET /api/v1/pipeline-runs/{run_id}​

Returns the run. Every poll re-signs the links.

Add ?include_content=true to also get each finished output's words. It adds a content object to every job: title, subtitle, preheader, text, body_html (line-break HTML for Mighty Networks or Gmail), and html (the full file, for HTML outputs). It also adds an outputs object keyed by template name, e.g. outputs.mighty_networks_article.content.body_html. The callback always includes content.

{
"run_id": "a82cc7cd-…",
"status": "done",
"file": {"id": "a82cc7cd-…", "filename": "…mp3", "status": "ready", "error": null},
"jobs": [
{
"job_id": "541d64d2-…",
"template_name": "Exchange Newsletter (HTML)",
"status": "done",
"title": "Senior In-House Counsel Exchange: October Notes",
"filename": "Senior In-House Counsel Exchange- October Notes.html",
"download_url": "https://…r2.cloudflarestorage.com/…",
"preview_url": "https://…",
"error": null
}
],
"completed": 7, "failed": 0, "total": 7
}
statusMeaning
transcribingThe file is being ingested / transcribed
generatingTemplates are running
doneEvery output is ready
partialSome outputs failed (see each job's error); the rest are ready
errorNothing was produced (e.g. the file could not be downloaded or had no speech)

download_url forces a save. preview_url opens inline. Both expire after 24 hours. Poll again for fresh links, or fetch the file in the next Zap step.

The outputs also appear in the project's Studio panel like any other generation.


POST /api/v1/pipeline-runs/{run_id}/retry​

Re-runs only the failed outputs of a finished run, on the transcript the run already has. It does no new download, no new transcription, and charges no transcription credits. The retried jobs keep their ids, so a Zap just replays its "collect outputs" step afterwards. A chained template whose parent succeeded the first time receives that parent's saved output.

  • Auth: X-API-Key (or a JWT), member or higher on the notebook.
  • Body (optional): {"inputs_from": {...}}. It defaults to the chain the run was submitted with.
  • Response 202: {run_id, status: "generating", credits_required, retrying: [{job_id, template_name, credits, input_from}]}.
  • Errors: 409 if the run is still in progress, has no failed outputs, or its source never finished ingesting (submit a new run in that case). 402 if the balance cannot cover the retried templates.

Credits​

  • Each template is priced as in Generation, including the model multiplier. At submit time the balance must cover every template, or you get a 402 and nothing happens.
  • Each template is charged when it starts running. A file that cannot be downloaded or has no speech costs nothing. A template that starts and then fails is not refunded, the same as single generation.
  • Transcription is metered separately, as for any audio upload: 1 credit per 2 minutes of audio.

Zapier recipe​

Zap 1 — submit

  1. Trigger: New File in Folder (Google Drive / Dropbox / OneDrive), or any step that yields an MP3.
  2. Action: Webhooks by Zapier → POST
    • URL: https://research.onfire.so/api/v1/notebooks/<NOTEBOOK_ID>/pipeline
    • Payload type: form
    • Data:
      • file_url → the file's direct download link from step 1. Alternatively, map the file itself to a file field.
      • filename → the file name from step 1
      • template_ids → a4ab7511-…,3306832b-…,80e827d0-…,8081e906-…,9261e63e-…,fd90aacd-…,efbb7d03-…
      • inputs_from → fd90aacd-…=9261e63e-…
      • callback_url → the Catch Hook URL from Zap 2
    • Headers: X-API-Key → your rsk_… key

Zap 2 — receive

  1. Trigger: Webhooks by Zapier → Catch Hook. Its URL is the callback_url above.
  2. Actions: use jobs[].download_url / jobs[].filename to save each output to Drive, email it, or post it to Slack. Use Zapier's Looping step to handle every job.
Prefer file_url over uploading the bytes

Zapier waits for the 202. With file_url the response is immediate, and the platform downloads the file afterwards. Uploading a large file directly makes Zapier hold the connection for as long as the upload takes.

Runs execute in the service process

A platform deploy during a run can interrupt it. The run then shows its jobs stuck in running. Submit it again.