Skip to content

Call an API from a Flow

Flows are great at collecting answers (Ask / Send / Buttons). The Call API node adds the missing piece: reading and writing your own backend during the chat. With it you can build order lookups, live pricing, and real transactions directly in the Flow builder instead of a separate bot.

In the Flow builder, open the palette → Data category → Call API. It has two output handles:

  • success — the request returned 2xx
  • error — non-2xx, a timeout, or any failure

Connect a fallback message to the error handle so the customer always gets a reply.

FieldNotes
MethodGET / POST / PUT / PATCH / DELETE
URLSupports {{variable}} — e.g. https://api.example.com/orders?phone={{phone}}
HeadersKey/value list. Each value is either a literal (with {{variable}}) or a 🔒 secret referenced by name (see below)
BodyJSON with {{variable}} interpolation (shown for POST/PUT/PATCH/DELETE)
Response mappingresponse path → variable. Dot/JSON-path (e.g. data.orders.0.id). Lists become newline-joined text — handy for a following Send message
TimeoutMilliseconds (default 10000, capped at 30000)

After the call, two variables are always set:

  • {{_http_status}} — the HTTP status code
  • {{_http_error}} — a short reason on the error branch (e.g. HTTP 404, request failed: ConnectTimeout)

Store credentials once at Settings → Flow Secrets (a name + value, e.g. infracart_api). In a header, switch the value to 🔒 secret and pick the name.

  • The raw value is write-only — it’s never returned by the API and never appears in the exported flow graph JSON.
  • The engine resolves the secret to its value only at run time, server-side.

Flows are authored by PATCH-ing draft_graph. A Call API node looks like:

http_request node
{
"id": "fetch_orders",
"type": "http_request",
"position": { "x": 600, "y": 140 },
"data": {
"method": "GET",
"url": "https://api.example.com/orders?phone={{phone}}",
"headers": [{ "key": "X-Api-Key", "secretRef": "infracart_api" }],
"body": null,
"responseMap": { "orders": "orders_text" },
"timeoutMs": 10000
}
}

Edges branch on sourceHandle: "success" and sourceHandle: "error":

edges
[
{ "id": "e1", "source": "fetch_orders", "sourceHandle": "success", "target": "show_orders" },
{ "id": "e2", "source": "fetch_orders", "sourceHandle": "error", "target": "fallback_msg" }
]

A header with an inline value instead of a secret:

{ "key": "Content-Type", "value": "application/json" }

Order status — buyer taps My orders:

Ask Buttons ("My orders")
→ Call API GET /orders?phone={{phone}} map: orders → {{orders_text}}
success → Send "Your orders:\n{{orders_text}}"
error → Send "Couldn't load your orders right now — try again."

Live rate:

Ask Text ("Which material?", save as material)
→ Call API GET /rate?product={{material}} map: price → {{rate}}
success → Send "Today's {{material}} rate: ₹{{rate}}"
error → Send "Rates are unavailable at the moment."

Submit a real bid:

… collect rfq + amount …
→ Call API POST /bids
body: {"rfq":"{{rfq}}","amt":{{amount}}}
header X-Api-Key = 🔒 infracart_api
map: rfq_no → {{rfq_no}}
success → Send "Quote submitted for {{rfq_no}} ✅"
error → Send "We couldn't submit your quote — please retry."
  • Never breaks the flow. Any failure/timeout/non-2xx routes to the error handle — the run never crashes.
  • Interpolation applies to URL, header values, and body via {{variable}}. Unknown variables are left as-is.
  • Body must be valid JSON after interpolation; otherwise it’s sent without a body.
  • Response mapping reads up to ~200 KB of JSON. A mapped list is joined into newline-separated text; an object is stored as compact JSON; scalars pass through.
  • Timeout is capped at 30 s so a slow API can’t hang the conversation.
  • Secret headers are stored at the workspace level and referenced by name — never the raw value in the graph.
  • Only fields you map end up in flow variables; nothing else from the response is stored.