--json, --quiet, --schema, and --dry-run on mutating commands) - see the overview.
Account
engini login
Store an Engini API key.
--api-key, the CLI offers to open the key-creation page and then prompts for the key. Input is masked (one * per character) and confirmed on success - ✓ Received 44 characters — eng_…wxyz - so you can tell a paste arrived, and arrived whole.
A key must start with eng_. An empty, malformed, or line-broken value is rejected on both entry paths: the prompt re-asks up to three times, --api-key fails immediately, and neither writes anything. Ctrl-C or Ctrl-D aborts. Every rejection exits 2 and leaves your existing credentials untouched.
Prompting needs a terminal on both stdin and stdout. If stdin is piped - CI, a wrapper script, engini login < /dev/null - pass --api-key; the CLI exits 2 rather than prompting into a stream nobody is typing into.
engini logout
Clear the stored credentials. Prints {"logged_out": true} (or "noop": true when nothing was stored).
engini whoami
Report the authenticated identity: full_name, email, current_company, role, user_type (apiuser for an API key, user for a JWT).
Tools
engini tools list
engini tools get <tool_id>
Full canonical detail for one tool - including input_schema, output_schema, and the supports_filters / supports_sort / supports_top_offset capability flags. (supports_top_offset means top and offset together; a tool can take SelectTop alone - the input_schema is the ground truth.)
engini tools call <tool_id>
Execute a tool against a connection.
--connection > --toolset > the application’s default. Default output envelope: {output, history_id, execution_info}; results larger than --max-bytes are stored locally and replaced by a handle.
engini tools result [handle]
Drill into a stored (sandboxed) result.
Applications
engini applications list
--search <query>, --available (only apps you can use), --limit <n>.
engini applications get <slug>
Adds supports_object_selection, help_url, and authentication_methods - how you discover --auth <id> and credential field names for connecting.
Connect
engini connect [<app>]
The guided way to create a connection - interactive on a TTY, agent-resumable when piped (see the machine contract).
{"connection_id", "is_alive", "objects_selected"}.
A field with no static list of legal values may still have a dynamically-discovered one after an OAuth sign-in (e.g. Salesforce’s ApiVersionUrl) - a numbered picker in a TTY, or --field <name>=<value> validated against the discovered set (exit 2 on a value that isn’t one of them). See Field options in need:"fields" and need:"auth".
Connections
engini connections list | get
--kind filters on Regular, Key, or MCPClient (case-insensitive). Key is exactly what the Connections page’s Keys tab shows - see Keys vs Integrations. MCPClient is accepted but returns nothing: this endpoint doesn’t serve that kind. OPA is rejected (exit 5) - on-prem agents are not connections on this surface; use engini opa. Every row carries a kind field.
Credential fields and metadata are deliberately omitted from get - they can carry secrets.
engini connections create <application>
Direct-credential connections (--auth <id>, --name <name>, repeatable --field key=value). OAuth2 apps use engini connect instead.
engini connections update <connection_id>
Change a connection’s name, description, credential fields, or metadata. Anything you don’t pass is left unchanged.
--oauth-state <state> re-authenticates an OAuth2 connection in place: the server derives fresh credentials from that sign-in’s record and applies them to the existing connection. Passing it alone - with no --field, --name, or --metadata - is still a valid edit, and is the only way to rotate an OAuth credential without minting a new connection id and re-pointing every workflow that references it.
--dry-run output names the risk explicitly, so you can see what you are about to drop before you drop it:
engini connections replace --workflow <id> --from <id> --to <id>
Point one workflow at a different connection.
903’s activities are rewired. Every other workflow using connection 481 is untouched. All three ids are required and named rather than positional - three same-typed integers are easy to transpose, and swapping --from and --to would point the workflow at the wrong credential.
Fails with exit 5 if an activity cannot be remapped, i.e. the target connection doesn’t expose the activity or object the workflow needs.
engini connections defaults | set-default | clear-default
The default connection is what a tool call resolves to when it doesn’t name one.
The asymmetry is deliberate:
set-default identifies the connection you want promoted, while clear-default identifies the application whose default you want removed - the connection is looked up for you.engini connections delete <connection_id>
Prompts on a TTY; requires --force non-interactively.
engini connections check <connection_id>
Liveness test. A conclusive failure exits 1; an inconclusive check reports is_alive: null and exits 0.
engini connections refresh <connection_id>
Async object refresh - waits by default (--no-wait to return immediately, --timeout default 300s). A failed refresh exits 1; a timeout exits 124.
engini connections objects | select-objects <connection_id>
engini connections sign-in-url | oauth-complete <application>
The OAuth primitives behind connect:
Toolsets
Manage toolsets - named bundles that pin which connection each application uses and which tools are permitted.engini toolsets list | get
engini toolsets create
Two ways to express the bindings, mutually exclusive:
engini toolsets update <toolset_id>
Same options as create (with --name as a rename). connections and workflows are replaced wholesale, not merged - send the full intended set. --dry-run fetches the current toolset and shows exactly what would be added, removed, or changed in scope before anything is written:
engini toolsets delete <toolset_id>
Prompts on a TTY; requires --force non-interactively.
MCP servers
Inspect and configure the MCP servers your account exposes - the endpoints an MCP client points at (see MCP client setup). Not theMCPClient connection kind, which is Engini consuming someone else’s MCP server.
MCP servers are account-wide, not per-user: an Engini API key sees every server on the account.
There is no
engini mcp create and no engini mcp delete - deliberately, unlike toolsets, which have both. Creating and destroying an MCP server stays in the engini.io UI, which is where the server’s SSE URL is issued and where its anonymous-access setting is chosen (is_anonymous_allowed is read-only over the API). The CLI maintains servers that already exist.To take a server out of service without destroying it, use engini mcp deactivate. It is reversible, and the server stays listed.engini mcp list
Every MCP server on the account. Deactivated servers are included, reporting is_active: false - they are not hidden and not deleted.
mcp_server_token, name, is_active, is_anonymous_allowed, connections[], workflows[], and created_date. Pagination is handled for you.
engini mcp get <mcp_server_token>
Full detail for one server: the list summary plus the SSE url and the audit fields updated_date, created_by_account_user_id, and updated_by_account_user_id.
4 with Unknown MCP server token. - the token itself is not echoed back.
engini mcp activate | deactivate <mcp_server_token>
Turn a server on or off. Both send only the is_active scalar, so no other field is read or rewritten and there is nothing to race with.
Deactivating is not a delete. The server keeps its token, its connections, and its workflows; it still appears in list and get; and activate puts it straight back. There is no confirmation prompt and no --force.
engini mcp connections add <mcp_server_token>
Expose a connection on the server, or re-scope one that is already exposed. Existing connections are preserved; an entry with the same connection id is replaced.
--connection 0x10 or --connection 1e3 exits 2 rather than binding a connection you never typed.
engini mcp connections rm <mcp_server_token>
Stop exposing connections, leaving the rest in place.
4. It does not succeed silently - a “removed” that left the binding exposed would be worse than an error.
engini mcp tools list <mcp_server_token>
The tools a connection makes available to this server - the candidates for its allow-list.
engini mcp tools set <mcp_server_token>
Replace the tool allow-list for one connection. Every other connection on the server is left untouched.
--tool and --all are mutually exclusive, and exactly one of them is required - either mistake exits 2. --all sends an empty allow-list, which the API reads as “every tool of the application”; that is why it is a separate flag rather than --tool with no value. The connection must already be on the server, otherwise exit 4 tells you to run engini mcp connections add first.
engini mcp workflows add <mcp_server_token>
Expose workflows as MCP tools. Existing workflows are preserved; an entry with the same workflow token is replaced.
workflowToken, not workflowId. Omit the overrides and the tool falls back to the workflow’s own slug and description.
--tool-name/--tool-description apply to a single --workflow. Passing either alongside two or more workflows exits 2: one name across several workflows would collide on the server’s unique tool name, and the CLI refuses rather than picking a winner.
engini mcp workflows rm <mcp_server_token>
Stop exposing workflows, leaving the rest in place.
4.
On-prem agents
Provision, inspect, configure, and credential your On-prem agents - the Engini component a customer installs behind their firewall to reach systems the cloud cannot: SQL Server, Oracle, Priority ERP, file shares. Agents are not connections on this surface.engini connections list never shows them, --kind OPA is rejected, and engini connections get <agent_id> is a 404. Everything about an agent lives under engini opa.
An agent is polled, never pushed to. It polls Engini for work on its own schedule; Engini never opens a connection to it. So a command that “talks to the agent” is really waiting for the agent’s next poll, and an agent that has not polled recently is unreachable regardless of what the API does.That is why
update reports applied_to_agent rather than failing, and why logs needs the agent Online. A freshly created agent is WaitingForConnection until it polls for the first time.engini opa list
Every on-prem agent on the account. Pagination is handled for you.
agent_id, name, status, version (what the agent last reported; null until it first connects), and last_seen_at (its last heartbeat; null until it first connects).
engini opa get <agent_id>
The list summary plus description, settings (parallel_tasks, pull_period_seconds, log_level), created_date, and updated_date.
4. So does an id that belongs to another account, or one that is a regular connection rather than an agent - the API answers 404 to all three on purpose, so the endpoint cannot be used to probe which ids exist.
engini opa create --name <name>
Register a new agent. Prints its token.
WaitingForConnection. Install the agent on its host with the token from the output; it turns Online on its first poll.
If the account has more than one on-prem-agent application (rare), create exits 5 with a message listing the candidate slugs - pass one back with --application-slug. That error is the only place to learn them: engini applications list does not show agent applications.
engini opa update <agent_id>
Change an agent’s name, description, or runtime settings. Takes the same --name, --description, --parallel-tasks, --pull-period, and --log-level flags as create. At least one is required (exit 2 otherwise).
Merges, does not replace. A flag you omit leaves that field exactly as it was - unlike engini connections update, whose --field replaces the whole map. --dry-run shows precisely what will be sent.
applied_to_agent-trueif the running agent acknowledged the change;falseif the change is stored but not yet delivered because the agent was notOnline.falseis not an error and does not exit non-zero. The agent applies the stored settings when it next connects; there is nothing to retry.dispatched_task_ids- the cloud-task ids handed to the agent, empty when nothing was dispatched.
parallel_tasks 1-100 and pull_period_seconds at least 10 - and an out-of-range value exits 5 with the message One or more agent settings are outside the range this agent accepts. The CLI does not duplicate those numbers, so they cannot drift.
engini opa delete <agent_id>
Permanently delete an agent. Workflows routed through it stop working. Confirms on a TTY; requires --force otherwise (exit 2 if neither).
engini opa enable | disable <agent_id>
Toggle whether the agent is marked online. Not a delete: a disabled agent keeps its token and settings and still appears in list. Disabling stops it being marked Online; tasks it already holds still complete. Enabling returns it to WaitingForConnection (it does not overwrite Online on an agent that is already up). No confirmation - both are reversible.
engini opa token get | rotate <agent_id>
get prints the agent’s current token as {"agent_id": 41, "token": "…"}.
rotate issues a new token and invalidates the old one immediately. Confirms on a TTY; requires --force otherwise. The output includes a note reminding you the agent must be reconfigured.
engini opa logs <agent_id> -o <file>
Collect the agent’s logs as a zip archive, written to -o. -o is required - there is no sensible default name for a customer’s log archive, and one would silently overwrite the previous pull. The bytes never go to stdout; the receipt does.
Online: otherwise exit 1 with OPA_AGENT_OFFLINE: … is WaitingForConnection; logs can only be collected from an Online agent. - retry once it has connected. If the agent does not answer in time, exit 124.
engini opa download
Fetch the current agent installer.
--json. When it carries a sha256, download verifies the file against it and refuses to write on a mismatch (exit 5). When the manifest is degraded (no checksum was published), the file is still written but the receipt says verified: false; a script that requires integrity should check that field. The checksum proves the download was not corrupted, not who published it - the manifest sits beside the installer.
Triggers
Fire on a change in a connected app. A trigger instance (ti_…) is an entity in its own right, created from a trigger type (a catalog slug) against one of your connections; its events are delivered to a destination (an HTTPS endpoint you own). Concepts: Triggers.
A trigger is created disabled.
create gives you a ti_…; nothing is captured until enable. And enable also clears a block — Engini blocks a trigger by itself after repeated failures, a deleted connection, or a provider that refused the subscription, and a blocked trigger stays silent until an enable clears it. The exception is an account over its activity limit, which is refused rather than unblocked.engini triggers types
The catalog of trigger types you can create an instance from.
engini triggers types get <trigger_slug>
One type’s config_schema, payload_schema, and parameter blocks. Read this before create — whether a type takes a schedule, listen columns, or neither is per type. --app <slug> disambiguates a slug carried by more than one application.
engini triggers list
Trigger instances on the account.
--include workflow returns one page only, whatever --top says. Designer-built triggers surfaced this way are not paginated yet. Raise --top to see more in the single page; do not expect to walk the full set with it.engini triggers get <trigger_id>
Status, schedule, and last-error detail for one instance — including the derived status, its status_reason, next_run_at for polling triggers, and subscription_count.
subscription_count is usually not 1. A provider subscription covers one watched column, so three --listen columns create three subscriptions — and providers meter them.engini triggers create <trigger_slug>
Connection-less types need no
--connection from 0.23.0. Types such as engini_schedule and http_webhook are created with the flag omitted. On 0.20.0-0.22.x the CLI refused without --connection and exited 2; upgrade rather than passing a placeholder id.--dry-run validates and prints would_create without calling the API.
engini triggers enable | disable <trigger_id>
enable starts capture and clears any block. disable stops capture; it is not a delete, and the instance and its event history stay.
engini triggers delete <trigger_id>
Permanent, and deletes the event history with it. Confirms on a TTY; requires --force otherwise.
engini triggers events <trigger_id>
The captured event log — the record of what fired and what happened to each delivery. --limit <n> caps the rows.
engini triggers events get <event_id>
One event plus its full delivery attempt history. This is where real delivery state lives — delivered, failed, or genuinely pending.
engini triggers events replay <event_id>
Re-deliver a captured event to its destination.
A DESTINATION_DISABLED error means the destination auto-disabled after too many failures. Run engini triggers destinations enable <name> first, then replay.
Replay queues one more attempt rather than delivering inline, and the attempt numbering continues from where it left off - so a replay gets whatever retries the event had left, none if it had already used all six. Read events get a few seconds later for the result. Walkthrough: Debug and replay a failed delivery.
engini triggers listen
Stream events live. For local development, not a delivery mechanism — production delivery is a destination plus signature verification.
listen prints one compact JSON line per event, always — the stream is line-oriented regardless of --json or whether stdout is a terminal, so it pipes into jq unchanged.The delivery field on the stream is always "pending". The stream reports dispatch, not delivery; read engini triggers events get <event_id> for the settled state.Ctrl-C stops listen and exits 0 from 0.23.0, after printing the summary line. On 0.20.0-0.22.x the interrupt was only noticed between stream frames, so an idle stream kept running - upgrade if you are on one of those. See the machine contract.engini triggers destinations set <url>
Set — or re-point — the account’s default destination. --name <n> names it; --max-failures <n> sets how many consecutive delivery failures auto-disable it (default 5).
Prints the signing secret only when this call creates the default. Re-pointing an existing default returns the destination with no secret.
engini triggers destinations add <name> <url>
Register a named destination. --default also makes it the account default. --max-failures <n> as above. Prints the signing secret.
engini triggers destinations list | show <name>
list is every destination on the account. show adds one destination’s failure counters and status. Neither ever returns the signing secret.
engini triggers destinations enable <name>
Re-enable a destination that auto-disabled, and reset its failure counter. Events are still captured while a destination is disabled — they just stop being delivered, so enabling and replaying recovers them.
engini triggers destinations rm <name>
Delete a destination. Deleting one that triggers still point at is a 409; --reassign <name> moves them to another destination in the same call.
The flag and the query parameter are spelled differently. The CLI flag is
--reassign; the API query parameter it maps to is reassign_to. If you are reading the API reference and the CLI side by side, this is not a typo in either.--force otherwise.
engini triggers destinations rotate-secret <name>
Mint a new signing secret. The old one stops working immediately - there is no overlap window, so any receiver still verifying with it starts rejecting deliveries. Update the receiver straight away, then replay whatever failed in between; the steps are in Debug and replay a failed delivery. Prints the new secret. Confirms on a TTY; requires --force otherwise.