Elsa Workflows · Developers
Build on Elsa Workflows
Start Elsa workflows from your own plugin, react when they start, or script the Elsa admin with a staff session. The Elsa credentials never leave the plugin.
What you can build
Plugins that start workflows
triggerElsaEvent, dispatchElsaWorkflow, or executeElsaWorkflow for the current site. The credentials stay inside Elsa Workflows.Reactions to starts
justflows.elsa-workflows.* hooks in a plugin, or subscribe a webhook to the same events.Staff tools
Workflows in Studio
justflows.forms.submitted.Base URL
https://your-site.example/api/elsa-workflowsThe older /ext/justflows.elsa-workflows address also works. Paths in this reference are relative to the base URL.
Who can call a route
Every route needs a staff session: the jf_session cookie, plus the capability listed on the route. Writes send the CSRF token as x-csrf-token. Each route also checks that the session belongs to the current site and that Elsa is turned on for that website. Routes that change the shared connection or a workspace server only answer on the installation’s main site.
Elsa Workflows has no API keys and no AI agent tools of its own. Other plugins use the SDK instead of these routes.
const ELSA = "https://your-site.example/api/elsa-workflows";
const csrf = document.cookie.match(/(?:^|; )jf_csrf=([^;]+)/)?.[1];
// Faulted runs on this site's tenant.
const { items } = await (await fetch(`${ELSA}/instances?status=faulted`, { credentials: "include" })).json();
// Start a published workflow again with the same correlation id.
await fetch(`${ELSA}/workflows/${items[0].definitionId}/start`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json", "x-csrf-token": csrf },
body: JSON.stringify({ confirm: true, correlationId: items[0].correlationId, input: {} }),
});Errors
Failures use one envelope with a stable code and a fixed message. Messages never include a response from Elsa, a URL, or a credential.
{ "error": { "code": "unconfigured", "message": "No Elsa connection is saved on this site." } }| Code | Status | Meaning |
|---|---|---|
unauthorized | 401 | No session. |
forbidden | 403 | The session lacks the capability, belongs to another site, or the route is for the main site only. |
feature_disabled | 403 | Elsa is turned off for this website. |
invalid | 400 | The input is not valid, such as an id, a page number, or workflow input over 16 KB. |
ssrf | 400 | The address is not allowed: not HTTPS on port 443, or a private or metadata host. |
unconfigured | 409 | This site has no Elsa connection. |
quota | 429 | The site reached its monthly workflow start limit. |
unsupported | 404 | The Elsa server does not offer that operation. |
rate-limit | 503 | Elsa asked the connector to slow down. |
auth, forbidden, tenant, timeout, tls, network, http, invalid | 502 | Elsa refused the call, could not be reached, or answered with something the connector does not understand. |
Rate limits
| Routes | Limit |
|---|---|
| Reads | 60 a minute |
| Starting a workflow | 20 a minute |
| Saving or removing a connection, workspace server, or comment workflow | 20 a minute |
| Testing a connection, and refreshing the overview | 10 a minute |
What reaches Elsa
Calls go to the Elsa 3 API under /elsa/api with the site’s tenant in X-Tenant-Id. Start requests send { versionOptions: "Published", input, correlationId }. Responses are allow-listed: workflow input, output, variables, and state are dropped before anything reaches the browser.