pipo. docs

Connectors

Every built-in connector and its with: settings, generated from the connector manifests that pipo check, the JSON Schema and the builder's forms all use.

Every block follows the same pattern: verb: connector, then with: for that connector's settings. pipo check validates each with: against the schema below; unknown keys are errors (P002).

  • Inputs: http, schedule, watch, push, pipeline, system, telegram, poll, tail, command, crawl
  • Taps: log, http, file, telegram, emit, exec
  • Transforms: map, http, exec
  • Agent providers: claude_api, claude_code, codex, pi, opencode
  • Outputs: sqlite, file, http, telegram, pipeline, stdout
  • Delivery checks: ack, external, none, record_exists, row_count, query, file_exists, file_nonempty, line_contains, checksum, status, downstream, follow_up

Inputs#

Set with input.via (or inputs.<name>.via). Guide: Inputs.

via: http#

Webhook or API call to /in/<pipeline><path>.

FieldTypeRequiredMeaning
pathstringRoute below /in/<pipeline>; default /. (default "/")
methodGET | POST | PUT | PATCH | DELETEHTTP method accepted; default POST.
authobjectRequest authentication: header + equals (a token header), or hmac: {header, secret, algorithm} (a body signature; sha256 by default).
publicbooleantrue takes requests from the network without auth when the engine is exposed (engine.host); without it such requests get 403. Loopback callers never need it.
respondaccepted | deliveredaccepted (default): answer 202 {packet_id} once journaled; delivered: wait for the final state up to timeout.
timeoutdurationHow long respond: delivered waits for delivery; default 30s.
listenintegerA port the runner binds itself on 127.0.0.1, so the input works without the engine gateway. (1–65535)

via: schedule#

Timer, by cron expression or fixed interval.

FieldTypeRequiredMeaning
cronstring5-field cron expression, evaluated in UTC. Exactly one of cron and every (P038).
everydurationFixed interval, counted from when the runner starts.
payloadanyThe data of every tick; default {}.

via: watch#

File system changes.

FieldTypeRequiredMeaning
pathstringyesGlob of files to watch, relative to the pipeline file; a plain folder means every file below it.
eventslist of create, change, deleteWhich changes become packets; default [create, change].
readcontent | pathpath (default): describe the file; content: also read its text into data.content.

Example data of one packet:

{
  "event": "create",
  "path": "/home/me/inbox/note.txt",
  "name": "note.txt",
  "size": 12,
  "mtime": "2026-01-01T09:00:00.000Z",
  "content": "hello there\n"
}

via: push#

Packets pushed by pipo push, the UI or an agent.

No with: settings.

via: pipeline#

Packets from another pipeline's to: pipeline output.

FieldTypeRequiredMeaning
fromlist of stringyesThe pipelines allowed to feed this input with their to: pipeline output; others are refused.

via: system#

Operating system samples.

FieldTypeRequiredMeaning
everydurationyesHow often to sample.
metricslist of cpu, memory, disk, battery, networkWhich metrics to sample; default all of them.

Example data of one packet:

{
  "cpu": {
    "percent": 12.5,
    "cores": 8,
    "load": [
      0.42,
      0.38,
      0.3
    ]
  },
  "memory": {
    "total": 17179869184,
    "free": 4294967296,
    "used": 12884901888,
    "percent": 75
  },
  "disk": {
    "path": "/",
    "total": 499963174912,
    "free": 199985269964,
    "used": 299977904948,
    "percent": 60
  },
  "battery": {
    "percent": 80,
    "status": "Discharging"
  },
  "network": {
    "rx_bytes": 123456789,
    "tx_bytes": 9876543
  },
  "sampled_at": "2026-01-01T09:00:00.000Z"
}

via: telegram#

Messages to a Telegram bot (long polling while the pipeline runs).

FieldTypeRequiredMeaning
botstringA bot name from <home>/bots.json (never a template); default the home's default bot.
tokenstringA bot token instead of a named bot, e.g. ${secrets.tg_token}.
allowlist of integerChat or user ids whose messages become packets; replaces the bot's own list. Messages from anyone else are ignored.
poll_everydurationHow long one long-poll waits for messages; default the bot's, else 25s, at least 1s.
downloadbooleanDownload attached files into the pipeline's folder; default true.

Example data of one packet:

{
  "message_id": 42,
  "date": "2026-01-01T09:00:00.000Z",
  "chat_id": 123456789,
  "chat_type": "private",
  "from": {
    "id": 123456789,
    "username": "ada",
    "name": "Ada Lovelace"
  },
  "text": "hello",
  "file": {
    "kind": "document",
    "file_id": "BQACAgQAAxkBAAI",
    "name": "report.pdf",
    "mime_type": "application/pdf",
    "size": 52000,
    "path": "/home/me/.pipo/pipelines/bot/files/report.pdf"
  }
}

via: poll#

Fetch a URL on a timer and emit what is new or changed.

FieldTypeRequiredMeaning
urlstringyesThe URL to fetch (http or https), a template rendered for each fetch with env, secrets and cursor.
cronstring5-field cron expression, evaluated in UTC. Exactly one of cron and every (P038).
everydurationFetch at start, then this long after each fetch; a restart waits out the rest.
methodGET | POST | PUT | PATCH | DELETEHTTP method; default GET.
headersmapRequest headers, templates rendered for each fetch (env, secrets, cursor).
bodyanyRequest body for methods other than GET and DELETE: a string as text, anything else as JSON.
timeoutdurationHow long one request may take; default 30s.
successlist of integer or stringStatus codes that count as success, as 204, "2xx" or "200-299"; default 2xx.
parseauto | json | text | feed | htmlHow the answer becomes data: auto (default, by content type), json, text, feed (RSS, Atom, JSON Feed) or html.
selectstringA CSS selector (parse: html): text and html hold only the matching elements.
itemsstringExpression over data, response and cursor giving the list to split into packets; default the whole answer (a feed's entries).
keystringExpression over item and data naming an element; default a feed entry's id, the URL for a whole answer, else a hash.
emitnew | changed | allnew: unseen keys; changed: also changed content; all: everything. Default changed for a whole answer, else new.
backfillbooleanfalse: take what the first fetch finds as seen and send only what comes later; default true.
rememberintegerHow many keys are kept; keys that stopped appearing are forgotten first. Default 10000. (≥ 1)
cursorstringExpression over data, items, response and cursor, saved after each fetch and available as ${cursor} in the next request.
nextstringExpression over data and response giving the next page's URL, followed in the same fetch.
max_pagesintegerThe most pages one fetch follows through next; default 10. (1–1000)

via: tail#

Lines appended to a file.

FieldTypeRequiredMeaning
pathstringyesThe file to follow, relative to the pipeline file.
parsetext | jsontext (default): {line, path}; json: the line parsed as JSON (others are skipped).
fromend | startWhere a first start begins: end (default, only new lines) or start (the whole file).

Example data of one packet:

{
  "line": "127.0.0.1 - - [01/Jan/2026:09:00:00 +0000] \"GET / HTTP/1.1\" 200 512",
  "path": "/var/log/app.log"
}

via: command#

Lines a long-running program prints.

FieldTypeRequiredMeaning
commandstringyesThe program to run (no shell), on PATH or a path relative to the pipeline file.
argslist of stringArguments, one string each, passed as they are.
cwdstringWhere the program runs, relative to the pipeline file; default its folder.
envmapEnvironment variables added for the program.
parsetext | jsontext (default): {line}; json: each stdout line parsed as JSON (others are skipped).
restartalways | on-failure | neverWhen the program exits: always (default), on-failure (an error exit or a signal) or never.

Example data of one packet:

{
  "line": "Jan 01 09:00:00 host nginx[42]: started"
}

via: crawl#

Crawl a site on a timer and emit the pages that changed.

FieldTypeRequiredMeaning
startanyyesThe URL, or list of URLs, each crawl starts from.
cronstring5-field cron expression, evaluated in UTC. Exactly one of cron and every (P038).
everydurationCrawl at start, then this long after each crawl ended.
withinanyURL prefixes a link must start with to be followed; default each start URL's origin.
excludelist of stringRegular expressions; a URL matching one is not followed.
depthintegerHow many links deep to follow from start (depth 0); default 3. (0–100)
max_pagesintegerThe most pages one crawl fetches; default 500. (1–100000)
delaydurationThe wait between requests; default 1s (a Crawl-delay in robots.txt can raise it).
selectstringA CSS selector: each page's text and html hold only the matching elements.
headersmapHeaders sent with every request (templates rendered at start).
timeoutdurationHow long one request may take; default 30s.
emitchanged | allchanged (default): pages that are new or changed since the last crawl; all: every page.
rememberintegerHow many pages are kept; pages that stopped appearing are forgotten first. Default 10000. (≥ 1)
robotsbooleanObey robots.txt; default true. Turn it off only for your own sites.

Example data of one packet:

{
  "url": "https://docs.example.com/guide/intro",
  "status": 200,
  "title": "Introduction",
  "text": "Welcome to the guide.",
  "links": [
    "https://docs.example.com/guide/install"
  ],
  "html": null,
  "depth": 1
}

Taps#

Side effects in the middle of the graph; data passes on unchanged. tap: fn.<name> calls a user function. Guide: Nodes and the graph.

tap: log#

Write a log line.

FieldTypeRequiredMeaning
leveldebug | info | warn | errorLog level; default info.
messagestringThe line to log (a template); default the packet's data as JSON.

tap: http#

Call an HTTP endpoint; data passes on unchanged.

FieldTypeRequiredMeaning
methodGET | POST | PUT | PATCH | DELETEHTTP method; default POST.
urlstringyesThe URL to call (a template).
headersmapRequest headers (templates). An Idempotency-Key: <key> header is sent unless you set one.
bodyanyRequest body (a template); default the packet's data. A string is sent as text, anything else as JSON. Not sent for GET and DELETE.
successlist of integer or stringStatus codes that count as success, as 204, "2xx" or "200-299"; default 2xx.

tap: file#

Append or write to a file.

FieldTypeRequiredMeaning
pathstringyesFile to write, relative to the pipeline file (a template).
formatjsonl | json | csv | textHow each packet is written; default jsonl.
modeappend | writeappend (default) or write (replace the file).

tap: telegram#

Send a Telegram message; data passes on unchanged.

FieldTypeRequiredMeaning
botstringA bot name from <home>/bots.json (never a template); default the home's default bot.
tokenstringA bot token instead of a named bot, e.g. ${secrets.tg_token}.
chat_idinteger or stringChat to send to; default the chat a telegram input's packet came from (required with any other input, P057).
textstringMessage text, or a file's caption; default data (as JSON when not a string), cut at 4096 characters.
photostringA photo to send: a path relative to the pipeline file, or an http(s) URL.
documentstringA file to send: a path relative to the pipeline file, or an http(s) URL.
parse_modeMarkdown | MarkdownV2 | HTMLTelegram formatting of text; off by default, so text from data can't break it.

tap: emit#

Emit a custom event.

FieldTypeRequiredMeaning
eventstringyesName of the custom event to record in the journal and the event stream.
detailanyData attached to the event (a template).

tap: exec#

Run a program on this machine; data passes on unchanged.

FieldTypeRequiredMeaning
commandstringyesThe program: a name found on PATH, or a path relative to the pipeline file. No shell.
argslist of stringArguments, each passed to the program as one argument (templates).
cwdstringFolder to run in, relative to the pipeline file; default the pipeline file's folder.
envmapExtra environment variables for the program.
stdinstringText fed to the program's standard input.
timeoutdurationKill the program after this long; default 5m.
successlist of integerExit codes that count as success; default [0].
resultinfo | text | jsonWhat a transform's data becomes: info (default; exit code, stdout, stderr, duration, files), text (stdout) or json (stdout parsed).
outputslist of stringFiles the program must write: their folders are created first, and a missing one is a node error.

Transforms#

Replace data with a result. transform: fn.<name> calls a user function. Guide: Nodes and the graph.

transform: map#

Replace data with the rendered with.data template.

FieldTypeRequiredMeaning
dataanyyesThe new data: a template, rendered per packet (a whole ${expr} keeps its type).

transform: http#

Call an HTTP endpoint; the response body becomes data.

FieldTypeRequiredMeaning
methodGET | POST | PUT | PATCH | DELETEHTTP method; default POST.
urlstringyesThe URL to call (a template).
headersmapRequest headers (templates). An Idempotency-Key: <key> header is sent unless you set one.
bodyanyRequest body (a template); default the packet's data. A string is sent as text, anything else as JSON. Not sent for GET and DELETE.
successlist of integer or stringStatus codes that count as success, as 204, "2xx" or "200-299"; default 2xx.

transform: exec#

Run a program on this machine; its result becomes data.

FieldTypeRequiredMeaning
commandstringyesThe program: a name found on PATH, or a path relative to the pipeline file. No shell.
argslist of stringArguments, each passed to the program as one argument (templates).
cwdstringFolder to run in, relative to the pipeline file; default the pipeline file's folder.
envmapExtra environment variables for the program.
stdinstringText fed to the program's standard input.
timeoutdurationKill the program after this long; default 5m.
successlist of integerExit codes that count as success; default [0].
resultinfo | text | jsonWhat a transform's data becomes: info (default; exit code, stdout, stderr, duration, files), text (stdout) or json (stdout parsed).
outputslist of stringFiles the program must write: their folders are created first, and a missing one is a node error.

Agent providers#

An agent: node's provider. Guide: Agent nodes.

agent: claude_api#

Claude via the Anthropic API (needs an API key).

Runs: a model API · default timeout: 60s

FieldTypeRequiredMeaning
modelstringyesModel id, e.g. claude-sonnet-5-5.
promptstringyesThe prompt (a template); use ${json(data)} to include the packet.
schemastringyesJSON Schema file the answer must match, relative to the pipeline file.
timeoutdurationHow long one call may take; default 60s.
max_tokensintegerMost output tokens per call; default 4096 (and never more than the packet's budget has left). (≥ 1)

agent: claude_code#

Your local Claude Code CLI (claude), logged in as you.

Runs: a local CLI (claude) · default timeout: 5m

FieldTypeRequiredMeaning
modelstringModel to ask the CLI for; default the CLI's own.
promptstringyesThe prompt (a template); use ${json(data)} to include the packet.
schemastringyesJSON Schema file the answer must match, relative to the pipeline file.
timeoutdurationHow long one call may take; default 5m.
cwdstringFolder to run in, relative to the pipeline file; default a fresh, empty folder per call.
allow_toolsbooleanLet the CLI use its tools (edit files, run commands) without asking; default false.

agent: codex#

Your local OpenAI Codex CLI (codex), logged in as you.

Runs: a local CLI (codex) · default timeout: 5m

FieldTypeRequiredMeaning
modelstringModel to ask the CLI for; default the CLI's own.
promptstringyesThe prompt (a template); use ${json(data)} to include the packet.
schemastringyesJSON Schema file the answer must match, relative to the pipeline file.
timeoutdurationHow long one call may take; default 5m.
cwdstringFolder to run in, relative to the pipeline file; default a fresh, empty folder per call.
allow_toolsbooleanLet the CLI use its tools (edit files, run commands) without asking; default false.

agent: pi#

Your local pi coding agent CLI (pi), with its configured providers.

Runs: a local CLI (pi) · default timeout: 5m

FieldTypeRequiredMeaning
modelstringModel to ask the CLI for; default the CLI's own.
promptstringyesThe prompt (a template); use ${json(data)} to include the packet.
schemastringyesJSON Schema file the answer must match, relative to the pipeline file.
timeoutdurationHow long one call may take; default 5m.
cwdstringFolder to run in, relative to the pipeline file; default a fresh, empty folder per call.
allow_toolsbooleanLet the CLI use its tools (edit files, run commands) without asking; default false.

agent: opencode#

Your local opencode CLI (opencode), with its configured providers.

Runs: a local CLI (opencode) · default timeout: 5m

FieldTypeRequiredMeaning
modelstringModel to ask the CLI for; default the CLI's own.
promptstringyesThe prompt (a template); use ${json(data)} to include the packet.
schemastringyesJSON Schema file the answer must match, relative to the pipeline file.
timeoutdurationHow long one call may take; default 5m.
cwdstringFolder to run in, relative to the pipeline file; default a fresh, empty folder per call.
allow_toolsbooleanLet the CLI use its tools (edit files, run commands) without asking; default false.

Outputs#

Set with output.to. Every pipeline has exactly one output. Guide: Outputs.

to: sqlite#

SQLite table.

Delivery checks: ack, external, none, record_exists, row_count, query · Batching: yes

FieldTypeRequiredMeaning
pathstringyesDatabase file, relative to the pipeline file.
tablestringyesTable to write to.
createbooleanCreate the table (with the key as PRIMARY KEY) when it's missing; default false.
modeinsert | upsertinsert (default): a repeated key is ignored; upsert: it updates the row.
keystringThe idempotency key column; default packet_id. It needs a PRIMARY KEY or UNIQUE constraint.
columnsmapColumn → template map; default the top-level fields of data plus the key column set to the packet's key.

to: file#

File on disk.

Delivery checks: ack, external, none, file_exists, file_nonempty, line_contains, checksum · Batching: yes

FieldTypeRequiredMeaning
pathstringyesFile to write, relative to the pipeline file (a template).
formatjsonl | json | csv | textjsonl (default; each line carries packet_id, so a repeated write is skipped), json, csv or text.
modeappend | writeappend (default) or write (replace the file).

to: http#

HTTP endpoint.

Delivery checks: ack, external, none, status, follow_up · Batching: no

FieldTypeRequiredMeaning
methodGET | POST | PUT | PATCH | DELETEHTTP method; default POST.
urlstringyesThe URL to call (a template).
headersmapRequest headers (templates). An Idempotency-Key: <key> header is sent unless you set one.
bodyanyRequest body (a template); default the packet's data. A string is sent as text, anything else as JSON. Not sent for GET and DELETE.
successlist of integer or stringStatus codes that count as success, as 204, "2xx" or "200-299"; default 2xx.

to: telegram#

Telegram message.

Delivery checks: ack, external, none · Batching: no

FieldTypeRequiredMeaning
botstringA bot name from <home>/bots.json (never a template); default the home's default bot.
tokenstringA bot token instead of a named bot, e.g. ${secrets.tg_token}.
chat_idinteger or stringChat to send to; default the chat a telegram input's packet came from (required with any other input, P057).
textstringMessage text, or a file's caption; default data (as JSON when not a string), cut at 4096 characters.
photostringA photo to send: a path relative to the pipeline file, or an http(s) URL.
documentstringA file to send: a path relative to the pipeline file, or an http(s) URL.
parse_modeMarkdown | MarkdownV2 | HTMLTelegram formatting of text; off by default, so text from data can't break it.

to: pipeline#

Another pipeline, fed through its via: pipeline input (§3.14).

Delivery checks: ack, external, none, downstream · Batching: no

FieldTypeRequiredMeaning
pipelinestringyesThe pipeline to feed; its via: pipeline input must list this one in from.
dataanyWhat to hand over (a template); default the packet's data.

to: stdout#

Standard output.

Delivery checks: ack, external, none · Batching: no

FieldTypeRequiredMeaning
formatjsonl | json | textHow each packet is printed; default jsonl.

Delivery checks#

Set with delivered.check. All with: values are templates, rendered per packet. Guide: Delivery checks.

Which output supports which check. pipo check rejects any other combination (P031).

Checksqlitefilehttptelegrampipelinestdout
ack✓✓✓✓✓✓
external✓✓✓✓✓✓
none✓✓✓✓✓✓
record_exists✓
row_count✓
query✓
file_exists✓
file_nonempty✓
line_contains✓
checksum✓
status✓
downstream✓
follow_up✓

check: ack#

The connector's own success report is enough.

No with: settings.

check: external#

Wait for an outside acknowledgement.

No with: settings.

check: none#

Don't verify.

No with: settings.

check: record_exists#

A row matching where exists.

FieldTypeRequiredMeaning
wheremapyesColumn → value pairs a row must match (with = and AND).

check: row_count#

query returns at least min rows.

FieldTypeRequiredMeaning
querystringyesA read-only SELECT.
paramslistValues bound to the query's ? placeholders.
mininteger or stringRows the query must return at least: a number, or a template that renders to one; default 1. (≥ 0)

check: query#

Custom SQL returns a truthy first column.

FieldTypeRequiredMeaning
sqlstringA read-only SELECT whose first column in the first row must be truthy.
querystringAlias of sql.
paramslistValues bound to the query's ? placeholders.

One of sql and query is required.

check: file_exists#

The file exists.

FieldTypeRequiredMeaning
pathstringFile to look for; default the output's own path.

check: file_nonempty#

The file exists and is not empty.

FieldTypeRequiredMeaning
pathstringFile to look at; default the output's own path.

check: line_contains#

A line contains value.

FieldTypeRequiredMeaning
pathstringFile to search; default the output's own path.
valuestringyesText one line of the file must contain, e.g. ${meta.packet_id}.

check: checksum#

The file hash equals sha256.

FieldTypeRequiredMeaning
pathstringFile to hash; default the output's own path.
sha256stringyesExpected SHA-256 of the file: 64 hex characters, any case.

check: status#

Write response status is in success.

FieldTypeRequiredMeaning
successlistStatus codes that count as delivered, as 204, "2xx" or "200-299"; default 2xx.

check: downstream#

The receiving pipeline delivered the packet.

No with: settings.

check: follow_up#

A GET to url succeeds, or its body matches match.

FieldTypeRequiredMeaning
urlstringyesURL to GET after the write.
headersmapHeaders for the follow-up request.
successlistStatus codes that count as success; default 2xx.
matchanyText the body must contain, or an object that must be a subset of the JSON body.
Generated from the Pipo source code.Pipo is licensed under the AGPLv3.