HTTP connector
The http connector calls an external HTTP(S) endpoint from a
service_task. This is the locked, as-built config contract — the same
schema the control plane validates on PUT /connectors/{action} and the
worker executes.
Config schema
{
"url": "https://...", // required; must start with http:// or https://
"method": "POST", // optional; GET|POST|PUT|PATCH|DELETE, default POST
"headers": { "X-Foo": "bar" }, // optional, passed through verbatim
"auth": { // optional; one of four kinds
"kind": "none"
// "kind": "bearer", "token": "..."
// "kind": "apiKey", "header": "X-API-Key", "value": "..." // header defaults to X-API-Key
// "kind": "basic", "username": "...", "password": "..."
// "secretRef" is reserved for a future Secrets Manager integration —
// v1 does not resolve it. If present without the matching inline
// secret, the call fails closed with Http.ConfigError.
},
"bodyTemplate": "...", // optional; empty = the input object verbatim.
// Tokens: {{input}}, {{input.a.b}}, {{$.a.b}}
// (a missing path resolves to null)
"responseMap": { "field": "$.a.b" }, // optional; empty = { connector, status, body };
// otherwise an object with only the mapped fields
"timeoutMs": 20000, // optional, default 20000, per request
"retry": { "max": 2, "backoffMs": 500 } // optional; retries only transport
// errors and 5xx — never 4xx
}
Secrets (auth.token, auth.value, auth.password) are stored inline
in the config in v1 — never as plaintext at rest, though: the whole config is
encrypted under the tenant’s envelope before being persisted (see
Encryption at rest). A future secretRef
pointing at a secrets manager is reserved but not implemented.
Auth kinds
kind | Fields | Behavior |
|---|---|---|
none (or omitted) | — | no auth header added |
bearer | token | Authorization: Bearer <token> |
apiKey | header (default X-API-Key), value | sets the named header to value |
basic | username, password | HTTP Basic auth header |
Request construction
- Body:
bodyTemplatemaps the input/state into the request body; an empty/absent template sends the input object verbatim. Available tokens:{{input}}(the whole input),{{input.a.b}}/{{$.a.b}}(a specific path — missing paths resolve tonull, never an error). - Response mapping:
responseMappicks specific fields out of the HTTP response via dot/JSONPath-style expressions into the task’s output object. With noresponseMap, the task result is{ connector, status, body }— the raw response wrapped with metadata. - Timeout & retry:
timeoutMs(default 20000) bounds each individual request;retry.max/retry.backoffMsgovern retries within this HTTP call only — transport errors and 5xx responses are retried up tomaxtimes with backoff; 4xx responses are never retried. This is independent of, and layered under, the workflow step’s ownretry/catch.
Error contract
Every failure maps to a typed Step Functions error name, so a workflow’s
retry/catch on the service_task can key off it precisely:
| Condition | Error name | Retries at the workflow level? |
|---|---|---|
Missing url / invalid config JSON / malformed auth / secretRef with no matching secret | Http.ConfigError | No |
Response 4xx | Http.ClientError | No |
Response 5xx | Http.ServerError | Yes (up to retry.max) |
| Transport deadline / timeout | Http.Timeout | Yes |
| Other transport error | Http.RequestError | Yes |
| Untyped error (other connector types, unknown type) | ConnectorError | Worker-dependent fallback |
Config-shape errors (a missing url, a malformed auth block, an unknown
auth.kind) are also rejected synchronously at bind time —
PUT /connectors/{action} returns 400 before the binding is ever saved,
so a broken config never reaches a running workflow in the first place. The
table above covers failures that only surface at call time (a
misconfigured but structurally valid secretRef, or the target endpoint
itself failing).
Example
curl -sS -X PUT "$RULEFLOW_API/api/connectors/disburse-funds" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"type": "http",
"config": {
"url": "https://payouts.acme.internal/v1/disburse",
"method": "POST",
"auth": { "kind": "bearer", "token": "sk_live_..." },
"bodyTemplate": "{ \"amount\": {{input.requested_amount}}, \"account\": {{input.account_id}} }",
"responseMap": { "payoutId": "$.id", "status": "$.status" },
"timeoutMs": 10000,
"retry": { "max": 2, "backoffMs": 500 }
}
}'
A service_task with "action": "disburse-funds" and a catch on
Http.ServerError/Http.Timeout will route to its error branch after
exhausting retries; a Http.ClientError (e.g. a 400 from the payout
service — a config problem) fails immediately without retrying, since
retrying an unchanged bad request would never succeed.