Skip to main content
FlowFn
IntegrationsTemplatesPricingDocsBlogSign inStart free
All documentation

Action bindings (single-action @keys)

UpdatedJun 5, 2026Reading time4 min read

What is an action binding?

An action binding lets a playground @key invoke one platform-tool action (e.g. Slack post message, Stripe create charge) or AI model call (e.g. OpenAI summarise) directly — without building a workflow around it. Useful when a single button needs to run a single action.

  • platform_tool_action — runs one action from a connected platform tool, using the team connection you select.
  • ai_tool_action — runs one AI model call. Use a team API connection (BYOK) or leave the connection empty to use the platform default key when your plan and the registry both support it.

Phase 1 limits: synchronous only (~25 s budget), no media-generating AI models (image/video/audio), no file-output actions.

Creating a binding

  1. Open the bottom Keys drawer and add a key. Pick type Platform action or AI action in the per-key toggle.
  2. Click the gear icon on that row and choose Configure action.
  3. Pick the Platform + Action (or AI registry + Model) and the team Connection the action should run with. Use the refresh icon next to Connection to pull a fresh list, or the open-in-new-tab icon to add a connection.
  4. For each declared input the action exposes, pick a source: From @key (snapshot a registered input/text/js_value @key at call time), Custom JS (a JS expression evaluated in the iframe before sending — same scope as your script, so it can read globals / DOM / top-level let/const bindings), Manual (you pass the value yourself when you invoke the action — from page JS or from a server function), Static value (you bake in a literal value), or Skip (only allowed for optional inputs).
  5. Each declared output is returned to the iframe by default. Uncheck any you want to hide from the public response.
  6. Save. An invocation code is minted — this is the bearer credential the iframe SDK uses; rotate it if it leaks.

Wiring an input to the snapshot

When you pick From @key for an action input, the binding declares a client-facing alias. By default the alias is the action's field key (e.g. text). The iframe must have a matching input / text / js_value @key with the same name so the SDK can snapshot a value into it.

If two bindings on the same playground both declare an input named text, give each one a unique alias in the drawer (e.g. summarise_text / classify_text) and create matching @keys for each.

Custom JS source

For values that aren't tied to a single @key — current timestamp, a computed property, the URL of the embedding page, a value derived from multiple fields — pick Custom JS in the source dropdown and write a JS expression like Date.now(), document.title, or cart.subtotal * 1.08. The SDK evaluates the expression in your script's lexical scope at call time (same path as js_value @keys), so it sees the top-level let / const bindings in your playground JS. The result is sent as the value of the declared input.

The expression runs every invocation — there's no caching. Use it for dynamic values; for constants use Static value instead.

Manual source

Pick Manual when you want to supply the value yourself at the moment you invoke the action, rather than tying it to a snapshot @key. It works the same whether the action runs from the page or from your server code:

  • From page JS: flowfn.invoke('send_email', { subject: 'Hi', html: body })
  • From a server function: ctx.tools.invoke('send_email', { subject: 'Hi', html: body })

The drawer lets you name the input the caller passes (e.g. html). Manual inputs are never auto-filled, so if you invoke the action without passing one, it's simply left out of the call. This is the natural choice for actions you drive entirely from code — for example building an email body on the server and sending it through a bound email action.

Auto-fire on click

Tag a button as a platform_tool_action / ai_tool_action @key (via Inspect, or via a selector in the Keys drawer). Clicks fire flowfn.invoke automatically with the snapshot values matching the binding's declared inputs. Listen for results via:

flowfn.onInvokeResult((res) => {
  if (res.ok) console.log(res.key, res.outputs);
  else console.error(res.key, res.error);
});

Invoking from JavaScript

Call flowfn.invoke(keyName, inputs) when you need full control — e.g. running an action on a non-click event or transforming inputs before sending. Returns a Promise:

const result = await flowfn.invoke('summarise', { text: 'hello world' });
// → { status: 'completed', key: 'summarise', outputs: { message: '...' } }

Unknown input names are rejected by the server. Missing required inputs trigger an invalid_inputs error. The full handler error is server-side only — the iframe sees a sanitised message.

Security

  • The invocation code is server-resolved — the iframe never sees which action runs, only its public alias. Treat it like any bearer credential; don't commit it to a public repo.
  • Inputs are validated against the binding's allowlist; extra fields are stripped silently.
  • Each (slug, IP) burst is rate-limited like other public surfaces. Persistent abuse trips the same 429s as form / trigger submissions.
  • If you delete a team connection that an action binding references, the dashboard refuses the delete and points you at the playground.

Spotted an issue or have feedback?

support@flowfn.com
Back to docs hub →