A connector lets a workflow call an external service — an HTTP API, an LLM, or any MCP
server. The call runs as a durable job: the step parks (WAITING_CONNECTOR_JOB), a worker
executes the call, and the run resumes with the result. Because it’s journaled, a connector call
is the right place to put external I/O (not a plain step handler).
The model: connection → instance → operation
- A connection is a registered service definition (its operations and their schemas).
- An instance is a per-workspace configuration of that connection — it holds the credentials (write-only; never returned).
- An operation is one callable action on the connection (e.g.
create_transaction).
flowctl conn list # connections: name, description, scope
flowctl conn ops <name> # operations on a connection
flowctl conn schema <name> <operation> # ONE operation's request_schema + response_schema
flowctl instance list --connection <name>
flowctl instance create <name> --name <instance> --credentials '<json>'
The connector() step
connector(name, opts) has no positional handler — the request and response are shaped by
named functions in opts.
function buildReq(ctx) { return { body: { id: ctx.vars.order_id } }; }
function readResp(ctx, response) { return { payment_id: response.body.id }; }
connector("charge", {
connection: "payment-api", // a registered connection
operation: "create_transaction",
instance: "primary", // a per-workspace instance (credentials)
requestMapper: buildReq, // fn(ctx) -> request matching the op's request_schema
responseCombiner: readResp, // fn(ctx, response) -> variable updates
onError: "handleError",
next: "end",
});
The one rule: match the adapter envelope
requestMapper(ctx) must return the object described by the operation’s request_schema;
responseCombiner(ctx, response) receives the object described by its response_schema. The
exact shape differs per adapter:
| Adapter | requestMapper returns | responseCombiner sees |
|---|---|---|
generic-http | { body } (+ optional path_params / query_params) | { status_code, body, headers } |
llm (OpenRouter) | { model, messages: [{ role, content }], … } | { id, model, choices, usage } |
composio | flat action args, e.g. { recipient_email, subject, body } | { data, successful, error } — check successful |
mcp | the MCP tool’s argument object | the tool’s result payload |
If your mapper/combiner doesn’t match the adapter envelope, the connector “succeeds” but writes
no variables — the #1 connector pitfall. When unsure, run
flowctl conn schema <connection> <operation> and match it exactly.
Failure handling
A connector step takes onError (and works with retryPolicy patterns described in
Errors & retries). A request that fails schema validation never creates a
job and routes straight to onError; a call that returns a failure routes there too, with
ctx.error populated.
Where to go next
- HTTP & LLM connectors — the two built-in adapters, with examples.
- MCP connectors — import any MCP server as a connector. (Pro)