Binions exposes a small, deliberate set of generic operations rather than thousands of app-specific connectors. Each operation is a verb you invoke from a playbook step (run: <daemon>.<operation>) or by emitting an Action event directly. Every operation maps to one Action message and produces a result Fact — a few fan out into per-message or per-item facts and close with a summary, and those are called out in their sections. This page is the complete reference for those operations — 64 of them across nine worker daemons, plus the showman daemon's twelve own-action page verbs.
About "API" here. Binions has no REST/OpenAPI surface for its daemons — the only HTTP endpoints any daemon serves are
/health/live,/health/readyand/metricson loopback (and theshowmandaemon's inbound HTTP ingress). The real, public API is this event-driven operation vocabulary, published as an AsyncAPI contract.
Each table below shows the playbook form you write and the result Fact the daemon emits when the operation succeeds. Under the hood the verb becomes exactly one Action message on the daemon's actions:<domain> inbox, and the Fact comes back on its events:<service> stream; the wire-level message names and payload schemas live in the AsyncAPI catalog. In a playbook step using mode: saga (the default), the step waits for the result Fact before the next step starts and makes it available as ${prev}. In mode: async, the step fires and moves on immediately; use a wait_for: step to join a specific result later. When an operation emits more than one fact, the optional step key expect: chooses which fact completes the saga step — a multicast webhook.send step must use expect: Webhook.GroupCompleted, while ai.batch completes on its AI.BatchCompleted summary automatically.
Failures are facts too. Mailbox, AI and analytics operations report every error path as
Mail.OperationFailed,AI.OperationFailedorAnalytics.OperationFailed— facts carrying a machine-readablereason— and the playbook engine rejects a broken playbook withPlaybook.Rejected. All of these are ordinary trigger events, so a fallback or alerting playbook can react to them like to any other fact.
Domain database · facts on events:database. Backends: PostgreSQL, MySQL, SQLite, MongoDB and SQL Server.
| Playbook verb | Result Fact |
|---|---|
database.register_table | Database.TableRegistered |
database.register_connection | Database.ConnectionRegistered |
database.write | Database.Inserted |
database.query | Database.QueryResult |
database.update | Database.Updated |
database.upsert | Database.Upserted |
database.delete | Database.Deleted |
database.transaction | Database.TransactionCompleted |
database.exists | Database.ExistsResult |
database.count | Database.CountResult |
database.aggregate | Database.Aggregated |
database.register_connection attaches a further, named backend at runtime — an alias plus backend and dsn/path — and every other database operation then accepts connection: <alias> to run against it (mutually exclusive with naming a backend inline; name neither and the operation uses the install's default PostgreSQL). The connection registry is in-memory, so register connections from a provisioning playbook triggered on System.Boot — secrets never persist. Inside database.transaction, a later operation can reference a value returned by an earlier write in the same transaction as "@tx:<op>.<column>" (PostgreSQL and SQLite).
Domain mail · facts on events:mailbox. Supports IMAP, MQTT and AMQP sources.
| Playbook verb | Result Fact |
|---|---|
mail.register_mailbox | Mailbox.Registered |
mail.unregister_mailbox | Mailbox.Unregistered |
mail.send | Mail.Sent |
mail.list_folders | Mail.FoldersListed |
mail.fetch | Mail.Fetched |
mail.move | Mail.Moved |
mail.mark | Mail.Marked |
mail.append | Mail.Appended |
Incoming mail is delivered as the Fact Mail.Received — no action needed. Register a mailbox and react to the fact from a playbook trigger. The folder operations — list_folders, fetch, move, mark and append — work against IMAP mailboxes. mail.fetch explodes a folder on demand: each pulled message becomes its own Mail.Received, in exactly the shape the live listener emits, so pull and push share the same downstream playbooks; the summary Fact Mail.Fetched then closes the operation as the saga response. Mail.Received carries the source folder on both paths. mail.append files a message into a folder — it stores, it does not send. Every mailbox failure is reported loudly as Mail.OperationFailed with the failing op and a reason.
Domain ai · facts on events:aiinjector.
| Playbook verb | Result Fact |
|---|---|
ai.classify | AI.Classified |
ai.extract | AI.Extracted |
ai.inject | AI.Generated |
ai.batch | AI.BatchCompleted |
ai.usage_report | AI.UsageReported |
The AI daemon implements classification, structured extraction, prompt injection/generation, batch processing and usage reporting. ai.batch runs a classify or extract over up to 200 items in one operation and emits a single AI.BatchCompleted summary — a failing item lands inside results[] without killing the batch. ai.usage_report reads the built-in usage ledger — calls, tokens and cost — grouped by provider, model or correlation. Actions can name an ordered providers fallback chain instead of a single provider, and every error path — unknown provider, timeout, rate limit, non-coercible output — is reported as AI.OperationFailed. These operations are a natural fit for mode: async fan-out patterns — fire multiple ai.classify steps concurrently and join only the results you need with wait_for:.
Domain webhook · facts on events:webhookcaller.
| Playbook verb | Result Fact |
|---|---|
webhook.register_endpoint | Webhook.EndpointRegistered |
webhook.send | Webhook.Delivered |
A single webhook.send yields Webhook.Delivered; set expect_json: true and the response body comes back parsed as response_json, with lower-cased response_headers on every delivery. A multicast send fans the same request out to a list of up to 32 registered endpoints (parallel or sequential): each member emits its own Delivered or Failed stamped with the group_id and endpoint, and the summary Webhook.GroupCompleted (total, ok, failed, per-endpoint results) closes the group — a saga step doing a multicast must declare expect: Webhook.GroupCompleted.
Domain schedule · facts on events:scheduler.
| Playbook verb | Result Fact |
|---|---|
scheduler.register_schedule | Schedule.Registered |
scheduler.deregister_schedule | Schedule.Deregistered |
scheduler.pause_schedule | Schedule.Paused |
scheduler.resume_schedule | Schedule.Resumed |
scheduler.trigger_now | Schedule.Fired |
scheduler.list_schedules | Schedule.Listed |
A schedule declares exactly one firing expression: cron_expr, interval_seconds, a one-shot at instant, or a dates calendar of instants. When a schedule fires — on its own grid or via scheduler.trigger_now — the daemon emits the Fact Schedule.Fired; react to it from a playbook trigger. One-shot at schedules and completed dates calendars deregister themselves automatically after their last fire, emitting Schedule.Deregistered just as an explicit deregister_schedule would.
Domain data · facts on events:datatransporter. Backends include S3/MinIO, SFTP, FTP, the local filesystem, MongoDB GridFS and write-only HTTP ingest sinks.
| Playbook verb | Result Fact |
|---|---|
data.upload | Data.Transported |
data.download | Data.Downloaded |
data.list_objects | Data.Listed |
data.delete_object | Data.Deleted |
data.move | Data.Moved |
data.copy | Data.Copied |
data.presign | Data.Presigned |
data.parse | Data.Parsed |
data.transform | Data.Transformed |
data.register_bucket | Data.BucketRegistered |
data.unregister_bucket | Data.BucketUnregistered |
data.move and data.copy compose any two registered buckets — a get, a put, and (for move) a delete through the bucket registry — so a file can flow from, say, SFTP to MinIO in one step. data.presign mints a time-limited download URL for an object (S3/MinIO buckets only; other backends fail loudly). data.parse extracts plain text from PDF, DOCX or text objects in-platform — text-layer PDFs only, no OCR. Every file-carrying fact exposes a flat bucket + key pair and every data operation accepts one, so steps chain naturally. Failures surface as loud Data.OperationFailed facts — a move whose source delete fails after a successful copy, an unsupported presign backend or a malformed document never fails silently.
Domain analytics · facts on events:dataanaliser.
| Playbook verb | Result Fact |
|---|---|
analytics.calculate_stats | Analytics.StatsComputed |
analytics.filter | Analytics.Filtered |
analytics.derive | Analytics.Derived |
analytics.emit_items | Analytics.ItemsEmitted |
analytics.detect_anomaly | Analytics.AnomaliesDetected |
analytics.forecast | Analytics.Forecasted |
analytics.rank | Analytics.Ranked |
analytics.filter applies the same condition grammar as playbook trigger filters — one shared evaluator, zero drift — to an in-flight row set; an empty result is a normal outcome. analytics.derive reshapes records through a declarative pipeline of typed numeric, string, object and array operations — no expression language. analytics.emit_items explodes a list into one Analytics.ItemEmitted fact per element — a trigger event, so a per-item playbook can filter with dot-paths into item — plus the closing Analytics.ItemsEmitted summary, which is the saga response; a batch is capped at 1000 items and fails loudly rather than truncating. Grammar and pipeline errors are reported as Analytics.OperationFailed.
Domain traefik · facts on events:traefiklinker. Supported route protocols: http, https, ws, wss, sse, grpc and grpc-web.
| Playbook verb | Result Fact |
|---|---|
traefik.register_route | Traefik.RouteRegistered |
traefik.unregister_route | Traefik.RouteUnregistered |
traefik.list_routes | Traefik.Routes |
traefik.reload | Traefik.Reconfigured |
Domain modbus · facts on events:modbus. Transport: TCP and RTU-over-TCP.
| Playbook verb | Result Fact |
|---|---|
modbus.read | Modbus.ReadSucceeded |
modbus.write | Modbus.WriteSucceeded |
modbus.read_device_id | Modbus.DeviceIdRead |
modbus.read_fifo | Modbus.FifoRead |
modbus.read_file_record | Modbus.FileRecordRead |
modbus.write_file_record | Modbus.FileRecordWritten |
modbus.register_plc | Modbus.PlcRegistered |
modbus.unregister_plc | Modbus.PlcDeregistered |
modbus.list | Modbus.PlcList |
modbus.set_server_registers | Modbus.ServerRegistersSet |
Beyond the request/response verbs, the daemon emits spontaneous facts: subscription polling raises Modbus.ValueChanged per changed chunk (with optional engineering decode, the physical value in new_value); configured thresholds raise Modbus.ThresholdCrossed on enter/exit edges only (hysteresis with a deadband — one fact per alarm episode edge); and in writable server mode, a register write by an external master raises Modbus.ServerRegisterWritten. modbus.set_server_registers populates the served register map — the platform computes, SCADA reads. modbus.write accepts verify: true to read a successful write back from the device; a mismatch is a loud Modbus.Failed.
Domain show · facts on events:showman. The showman daemon builds and serves live browser pages, templates and assets; its twelve verbs are own-action verbs — both emitted and handled by showman itself, letting playbooks drive the UI directly. Their result Facts and payloads are in the AsyncAPI catalog.
| Playbook verb | What it does |
|---|---|
show.register_page | Register a live page at a path. Pages materialize as pages/<path>/index.html; GET /path redirects (307) to /path/. |
show.unregister_page | Remove a registered page. |
show.update_page | Replace a page's content from one of three exclusive sources: inline content, a content_url, or {template, data} — re-render a registered template at the page's own registered path, refreshing the cache. |
show.list_pages | List the registered pages. |
show.set_index_menu | Set the index menu entries. |
show.register_template | Register a named minijinja template; the source goes in template_source. |
show.render_template | Render a registered template — data is the template context — to output_path, a page path with a leading /; extensionless paths serve at <path>.html. |
show.upload_asset | Materialize an asset file into the served root from content_inline, content_b64 or content_url. The asset name must carry a real extension — the served Content-Type derives from it. |
show.delete_asset | Remove an asset file from the served root. |
show.invalidate_cache | Drop cached page output. |
show.preload_cache | Warm the page cache ahead of traffic. |
show.status | Report the daemon's status. |
Pages serve inline <script> blocks byte-identical and templates keep script markup verbatim — inject data into JavaScript script-safely with {{ data | tojson }}, while plain {{ }} stays HTML-autoescaped.
showman also hosts the platform's inbound HTTP ingress: a request to an /in route arrives as the Fact Http.Received — a playbook trigger like any other. A route can require an HMAC-SHA256 signature over the raw request body (configured per route in the daemon's config, with the secret re-read on every request so rotation needs no restart); a request that fails the check is answered with 401 and audited as Http.Rejected, without ever reaching Http.Received. Live pages add one more inbound trigger: a browser frame on a page’s WebSocket channel arrives as Fact.Showman.WsMessage (channel, payload, per-connection client id).
Two more daemons contribute to the event surface without exposing generic verbs. The playbook engine emits lifecycle facts for every run — Playbook.Started, Playbook.Completed, Playbook.Failed — and rejects a playbook that parses but cannot execute (an unknown verb, a reference to an undeclared step id, a missing secret) at load time with Playbook.Rejected {name, file, errors[], source, kept}. The last good version keeps serving, and the fact is a trigger event — wire an admin-alert playbook to it. The same static checks run offline with binions-cliconsole validate --dry-run <file>. The logger, with alerting enabled in its configuration, watches error rates: more than a threshold of error-level entries from one producer within a time window emits a single Logs.ErrorRateExceeded {producer, count, window_secs, threshold}, with a per-producer cooldown.
Operations are invoked from playbook steps, which can match and reference event data. The filter operators available in trigger conditions, until: guards and analytics.filter conditions (one shared evaluator) are: .eq .ne .contains .startswith .endswith .gt .ge .lt .le .in .not_in .is_null .is_not_null plus the predicate has_attachments; a bare key with no operator suffix means equality. All top-level entries are AND-ed together, and the reserved key or: holds a flat list of branches (each itself a small AND-map) — the filter passes when the top-level entries match and at least one branch does. Step arguments interpolate values with ${trigger.X} (a field of the triggering event), ${prev.X} (the previous step's result in saga mode), ${steps.<id>.X} (a named step's result), ${loop.<var>} (the current loop counter) and ${secret.KEY} (a credential — allowed only in provisioning playbooks). Interpolation reads the result fact's payload directly — there is no .result wrapper — with one exception: ai.extract nests its extracted fields under result, as in ${steps.extract.result.total}.
Any of the operations above can be invoked with one of the four step variants. A brief reminder:
run: — invoke one operation. In saga mode (default) the step waits for the result fact; in async mode it fires and continues immediately.parallel: — run a list of run: steps concurrently within a single saga step. Single-level only.loop: — repeat a body of run: steps a bounded number of times, with an optional early-exit until: condition.wait_for: — in async mode, block until a specific result Fact arrives and match it by causation id.For full syntax and examples see Playbook anatomy.