JavaScript API
flowtrux.js - run workflows, submit forms, read the latest result and call declared hosts from an app's own code.
Every app can load the FlowTrux runtime, a small script with no dependencies. The starter templates already include it; the assistant uses it when it writes the app. This page is for authors who edit an app's code by hand.
<script src="/apps-runtime/v1/flowtrux.js"></script>
<script src="app.js"></script>
Your own code goes in files (app.js above) - inline scripts and on…= attributes do not run in an app.
All functions return promises. A failed call rejects with { code, message }; the message is safe to show a visitor.
FlowTrux.app
{ id, slug } of the current app.
FlowTrux.run(workflowId, fields, { wait })
Starts a workflow whose App trigger names this app and passes fields as trigger.data.fields.
const run = await FlowTrux.run("WORKFLOW_ID", { vehicle: "AB-123" });
// → { executionId, status, link }
workflowIdis the workflow's id (shown in the workflow's address, and in the card when the assistant creates it).fieldsis a flat object of simple values (text, numbers, booleans), up to 50 keys.- With
wait: true(the default) the call waits a short while for the run to finish; otherwise it returns at once. Either way, passlinktoFlowTrux.result.
FlowTrux.result(link, { intervalMs, timeoutMs, onUpdate })
Polls a run until it completes or fails.
const res = await FlowTrux.result(run.link, { onUpdate: (r) => showStatus(r.status) });
if (res.status === "COMPLETED") render(res.result);
result is the output of the run's last completed step. A failed run returns status: "FAILED" with a generic error. The link is valid for 24 hours.
FlowTrux.submit(fieldsOrFormData, { wait, workflowRef })
Sends a submission to an app that declares fields (see Forms & Submissions). It is checked against the declared fields, stored in the Submissions tab, and handed to every workflow bound to the app.
const answer = await FlowTrux.submit(new FormData(formEl), { wait: true });
// → { submissionId, started, link?, executionId?, status? }
- A
FormDatamay carry files; a plain object is sent as JSON. wait: trueruns the answering workflow (the form's Result workflow, orworkflowRef) and returns itslinkforFlowTrux.result.validation_failedcarrieserrors: [{ field, message }].
FlowTrux.form.bind(formEl, { wait, workflowRef, onSuccess, onError })
The easiest way to wire a <form>: it submits with FlowTrux.submit and handles the states for you.
- Sets
data-state="submitting" | "success" | "error"on the form. - Writes field errors into elements marked
[data-error-for="<name>"]and marks the controldata-invalid. - Writes a general error into
[data-flowtrux-form-error]. - On success calls
onSuccess(result), or, without it, hides the form and shows[data-flowtrux-success].
Returns a function that unbinds the form.
FlowTrux.latest()
Reads the latest result of the app's source workflow (Settings > Source workflow).
const { executedAt, data } = await FlowTrux.latest();
if (!executedAt) showEmpty(); else render(data);
data is what the page may read: the names listed under What the page may read, or the output of the source run's last step.
FlowTrux.autoRefresh(fn, seconds, { immediate })
Calls fn(latest) now and then every seconds (10–3600). It pauses while the tab is hidden and refreshes when it is shown again; an error skips one tick. Returns a stop() function. Pass { immediate: false } when the page has already called latest() itself.
FlowTrux.fetch(url, init)
Calls an outside service that does not accept requests from browsers, on behalf of the visitor - for example with a token the visitor typed or got by signing in to that service. It works like the browser's fetch and resolves to a normal Response with the service's status and body.
try {
const res = await FlowTrux.fetch("https://api.example.com/v1/items", {
headers: { Authorization: "Bearer " + visitorToken },
});
const items = await res.json();
} catch (err) {
if (err.code === "host_not_declared") console.warn("Declare", err.host, "in Settings");
}
- Only hosts listed in Settings > External hosts can be called, over
https. - Request bodies are text: a string,
URLSearchParams,FormDatawithout files, or a plain object (sent as JSON). - It rejects with an
Errorwhosecodeis one ofhost_not_declared(witherr.host),response_too_large(witherr.limitBytes),upstream_timeout,upstream_failed,blocked_url,rate_limited,invalid_request,request_too_large,unsupported_media_type,not_found. A service's own error (a 401 or 500) still resolves as aResponse- checkres.ok.
Use FlowTrux.fetch for the visitor's own accounts. For your organization's data and connected accounts, use a workflow and FlowTrux.run - nothing of your organization's is added to a FlowTrux.fetch request.
Error codes
Runtime calls reject with one of: not_found, invalid_body, validation_failed, not_bound (the workflow's App trigger does not name this app), run_limit (the app's runs-per-hour limit), execution_limit (your plan's monthly executions), busy, closed (the form is closed), payload_too_large, no_source (no source workflow), result_too_large, failed, timeout, network.
Limits you can set
In Settings:
| Setting | Default | Meaning |
|---|---|---|
| Runs per hour | 60 | Runs and submissions of this app per hour, all visitors together (1–10 000). |
| Reuse an identical result (minutes, 0 = off) | 0 | Return the earlier result when the same input arrives again within this time. |
| Calls to declared hosts per minute | 120 | FlowTrux.fetch, all visitors together (10–1000). |
| Largest answer from a declared host (MB) | 10 | Up to 25. "Ask the service for smaller pages instead of raising this." |
Runs started from an app count toward your plan's monthly executions like any other run.
External hosts and libraries
- Settings > External hosts - "The app's browser code may call only its own address and these hosts. Bare hostnames, e.g. api.open-meteo.com." A subdomain is a different host and must be listed separately.
- Libraries in the app - libraries the assistant added (for example a chart or map library) are copied into the app's own files, so the app loads nothing from outside CDNs.
- Images may come from any
httpsaddress.