What the external API is for
The Server tab's functions can be called from inside your published page with flowfn.server.call. The external server-function API lets you call those same functions from outside FlowFn — your own backend, a script, a scheduled job, or another system — over authenticated HTTP. Use it to let another system trigger the work a function does (write a data sheet, start a workflow or agent, run one of your bound tools) without shipping any of your logic or secrets to that system. The function still runs on FlowFn, as your team.
This is a separate, opt-in path from the in-page flowfn.server.call — see Two separate ways to call a function at the end.
Turning it on
The external API is off by default. Two things have to be true before a call will run:
- Your plan must include it. The external server-function API is a plan feature; if your plan doesn't include it you won't be able to switch it on. (Server-side code itself must also be on your plan, since the API runs your server functions.)
- You enable it on the playground. Turn the external API on for the specific playground you want to expose. It stays off until you opt in, and only the functions you've declared and left Enabled in the Functions panel are reachable.
The endpoint
Call a function with a real HTTP request to your app's domain:
GET | POST | PUT | PATCH | DELETE https://<your-app-domain>/api/app/playgrounds/<slug>/functions/<invocation_code>
<your-app-domain>— the domain your app is served on (your*.flowfn.comsubdomain or your custom domain).<slug>— the playground's slug.<invocation_code>— the target function's per-function invocation code (see below).
You pick the HTTP verb. Whatever verb you send, the function receives it as ctx.method, so a single function can handle several verbs or reject the ones you don't want.
Authentication
Authenticate with your App's credentials — the same pair the workflow API uses — sent as request headers:
x-app-code: <your app code>
x-api-key: <your app api key>
Both live on the App that owns the playground. Treat the API key like a password, and rotate it on the App if it ever leaks. A request with missing or wrong credentials is rejected before it reaches your function.
The per-function invocation code
Each server function has its own invocation code — the bearer that names which function a request runs. Open the Functions panel on the Server tab to see a function's code and to rotate it (the old code stops working immediately; every function has its own code, so rotating one leaves the others untouched). A request whose code is unknown, or belongs to a disabled function, gets the same generic "not found" as an unknown playground — the API never reveals which check failed.
ctx.method and ctx.query
On this path your function receives two extra values on ctx:
ctx.method— the HTTP verb you called with, uppercased ('GET','POST', …).ctx.query— the parsed URL query string as an object, always present regardless of verb.
On the in-page flowfn.server.call path these default to 'POST' and {}, so the same function works either way.
How arguments map
The function's declared inputs (in the Functions panel) are validated exactly as they are for an in-page call — unknown keys are rejected. Where those inputs are read from depends on the verb:
- POST / PUT / PATCH — from the JSON request body. Send the fields directly (
{ "email": "a@b.com" }) or wrapped as{ "args": { … } }; both are accepted. - GET / DELETE — from the URL query string (
?email=a@b.com).
Whichever verb you use, ctx.query also always holds the parsed query string, so a POST can still read query parameters.
The response
On success you get 200 with { "result": …, "logs": [ … ] } — result is whatever your function returned, and logs are its console lines. On failure you get an error status with { "error": { "code", "message" }, "logs" }: for example invalid_inputs when a declared field fails validation, rate_limited when you exceed the per-app rate limit, or the generic not-found when a gate is off. Any message you throw inside the function is passed back so you can surface it to the caller.
Two separate ways to call a function
The external API and the in-page call are independent and separately controlled — enabling one, or rotating one code, never affects the other:
- In-page (
flowfn.server.call) — first-party only: it runs from your own published page's JavaScript. It resolves a function by name and is authorized by the playground's single global public invoke code (rotatable on the playground). It carries no app credentials. - External API (this page) — authenticated with your App's
x-app-code+x-api-key, addresses a function by its per-function invocation code, and uses a real HTTP verb. It's for other systems, not the page.
A function you expose to the external API is not automatically callable from the page, and vice versa — each path has its own switch and its own code.