End users
Run workflows for your product's customers, poll and cancel their runs, request website login links, and erase a customer.
These routes let a product act for its own customers (end users). They exist only while the deployment runs with
FLUXUS_END_USERS=true; see End users and memory for the concepts.
Product gateway key
Every route on this page requires a product gateway key (scope: "end_user_gateway"), sent as
Authorization: Bearer hise_…. An Admin or Owner creates it with the session endpoint POST /api/api-keys:
{
"workspaceId": "ws_123",
"name": "Telegram bot",
"scope": "end_user_gateway",
"allowedWorkflowIds": ["wf_abc"]
}
allowedWorkflowIds must list at least one workflow of that workspace. The scope and the allowlist are fixed at
creation; to change them, create a new key and revoke the old one. A product gateway key:
- runs only its allowed workflows, and only for an end user;
- reads a run only through the gateway status route below, only for runs it started, and only for the end user that owns the run;
- is refused (
403) by every other v1 route and by the MCP endpoints; - stops working when its creator loses explicit workspace access, and while end users are disabled (
403).
A product may create at most 60 new end users per key per minute; the next call answers 429 with Retry-After.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/workflows/{id}/execute | Run an allowed workflow for an end user (queued) |
| GET | /api/v1/end-users/runs/{id} | Status and outcome of one of the key's runs |
| DELETE | /api/v1/end-users/runs/{id} | Cancel one of the key's runs |
| POST | /api/v1/runs/{id}/rerun | Re-run one of the key's runs for the same end user |
| POST | /api/v1/end-users/connect-requests | One-use website login link for an end user and a site |
| GET | /api/v1/end-users/connect-requests/{id} | Poll whether the customer saved that new login |
| POST | /api/v1/end-users/erasures | Erase everything stored for an end user |
| GET | /api/v1/end-users/erasures?id={id} | Poll an erasure |
End user IDs
endUser.externalId is the ID your product uses for the customer: 1–128 characters of letters, digits, and
. _ - : @ | + =. Routes that do not take a JSON body read it from the X-End-User-Id header, so it never appears in
a URL. Treat it as customer data: Confluye never writes it to audit events or logs.
Run a workflow for an end user
POST /api/v1/workflows/{id}/execute
{
"endUser": { "externalId": "tg-12345" },
"message": "Switch the form to the buyer I mentioned",
"input": { "prompt": "Switch the form to the buyer I mentioned" }
}
| Field | Required | Description |
|---|---|---|
endUser.externalId | Yes | The customer the run acts for. A new ID creates the end user. |
message | Yes | The customer's message, 1–32,000 characters and not blank. With the reply block's output it forms the turn recorded in memory. |
input | No | The workflow input, as for any execution. Pass the message here too when blocks read it from input. |
The end user is the identity of the run, not part of input: a block or a model cannot change it, and an
endUserId field inside input is ignored. End user runs always execute the active version: dryRun: true or
target: "candidate" answers 400, and so does a missing or blank message. Idempotency-Key is supported and
covers endUser, message, and input: repeating a key with a different message answers 409
(code: "idempotency_replay_mismatch") instead of replaying the earlier run. A stored receipt belongs to that end user,
so an erased customer's key is never replayed for a new customer with the same ID.
202 — the run was queued:
{
"run": {
"id": "run_1",
"workflowId": "wf_abc",
"status": "queued",
"startedAt": "…",
"finishedAt": null,
"durationMs": null
},
"queued": true,
"status": "queued",
"active": true,
"pollAfterMs": 1000,
"links": {
"self": "/api/v1/end-users/runs/run_1",
"cancel": "/api/v1/end-users/runs/run_1"
},
"statusUrl": "/api/v1/end-users/runs/run_1",
"cancelUrl": "/api/v1/end-users/runs/run_1",
"cancelMethod": "DELETE"
}
Start and rerun receipts include the same safe outcome fields as polling: status, active, pollAfterMs,
and output, error, or connectionRequired when available. They never include inputs, steps or logs.
An occupied Claude Code or Codex credential does not delay acceptance of an end user run. The worker checks the CLI login before execution, so an accepted run can still fail if that login is unavailable. Poll the returned run ID to follow its outcome.
Runs of one end user start in the order they were sent; each waits until the previous turn recorded its reply. A turn that parks on an approval or a timer releases the order: the next run starts and sees the parked message as pending.
Poll or cancel a run
GET /api/v1/end-users/runs/{id} and DELETE /api/v1/end-users/runs/{id}, both with the X-End-User-Id header.
A run is visible only when its ID, the key that started it, and its end user all match, and that end user is not
being erased; otherwise the answer is 404. Runs of an erased end user are never visible, even after the same
external ID starts a new end user.
{
"run": {
"id": "run_1",
"workflowId": "wf_abc",
"status": "succeeded",
"startedAt": "…",
"finishedAt": "…",
"durationMs": 5120
},
"status": "succeeded",
"queued": false,
"active": false,
"output": { "answer": "…" },
"error": null,
"links": { "self": "/api/v1/end-users/runs/run_1", "cancel": "/api/v1/end-users/runs/run_1" },
"cancelMethod": "DELETE",
"pollAfterMs": null
}
Keep polling while active is true (queued, running, or waiting), after pollAfterMs. The response never
contains the workflow definition, steps, or logs. DELETE cancels a queued, running, or waiting run and returns
the same shape; cancelling a finished run answers 409, and cancelling an already cancelled run returns it unchanged.
Every response of these routes, including 400, 401, 403, and 404 answers, is Cache-Control: no-store with
Vary: Authorization, X-End-User-Id.
Website login required
When a block needs the customer's website login and it is not usable, the run ends failed without retries and the
status response carries:
{ "connectionRequired": { "reason": "expired", "siteKey": "centris" } }
reason | Meaning | New login link? |
|---|---|---|
missing | The customer never signed in to this site | Yes |
expired | The saved login needs the customer to sign in again | Yes |
rejected | The website rejected the saved login | Yes |
revoked | The previous saved login was disconnected | Yes, with a fresh login and save |
grant_missing | A workspace Admin or Owner has not authorized this block for the site | No |
For missing, expired, rejected, and revoked, your product can request a new link automatically when the
customer asks for something requiring that website. Keep the original request, send the link, and wait for the
customer to sign in and save access before sending the turn again. The previous login stays unusable; issuing a
link never restores it. grant_missing requires authorization from a workspace Admin or Owner.
Re-run
POST /api/v1/runs/{id}/rerun with the X-End-User-Id header re-runs one of the key's runs for the same end user.
201 returns the safe outcome projection plus statusUrl, cancelUrl, and cancelMethod. A run of another key or end user answers 404; a
run whose end user was erased answers 409, and one whose end user is being erased answers 409 with
code: "erasing" and Retry-After.
Website login links
POST /api/v1/end-users/connect-requests
{ "endUser": { "externalId": "tg-12345" }, "siteKey": "centris" }
201 — { "requestId": "…", "connectUrl": "https://…", "expiresAt": "…" }. Send connectUrl to the customer; it
works once and expires after 15 minutes. It signs the customer in to that website only and never grants a Confluye
session. A new link cancels the customer's previous link for the same site. Responses are Cache-Control: no-store
and are never replayed from an idempotency cache.
Poll GET /api/v1/end-users/connect-requests/{requestId} with X-End-User-Id set to the same external ID.
200 returns only { "requestId": "…", "status": "pending" | "connected" | "expired" | "cancelled" }.
connected means the customer saved this request's login and it is still usable. A completed request whose
login was disconnected or replaced cannot continue the action. Requests outside the key owner's workspace,
for another customer, or without a live website grant on a workflow allowed by the key answer 404.
Responses are Cache-Control: no-store with Vary: Authorization, X-End-User-Id; they contain no login link,
browser state, or connection ID. Polling creates no customer or connection and does not consume the link.
Use a durable Wait between polls and a bounded deadline, such as 15 minutes. On connected, submit the retained
customer request once and poll that new run. On cancellation or expiry, stop and explain that sign-in was not
completed. The platform does not automatically retry the original failed run or reconnect a revoked workflow grant.
Erase an end user
POST /api/v1/end-users/erasures
{ "endUser": { "externalId": "tg-12345" } }
202 — the erasure started, or was already running or finished (repeating the request is safe):
{
"erasure": { "id": "eu_1", "status": "erasing" },
"links": { "self": "/api/v1/end-users/erasures?id=eu_1" },
"pollAfterMs": 2000
}
Poll GET /api/v1/end-users/erasures?id={id} until status is erased (pollAfterMs is then null). Erasure
cancels the customer's open runs and deletes their conversation, memory, work in progress, pending site notes, and
website logins in every workflow of the workspace. From the moment it starts, a new run for the same ID answers
409 with Retry-After. Once it finishes, the same ID starts a new end user with no history. While the erasure waits for a website login to be deleted
at the browser provider, the erasure object includes pendingReason: "browser_revocation"; keep polling.
Errors
| Status | When |
|---|---|
400 | Invalid JSON or fields; missing endUser.externalId; missing or blank message on an end user run; invalid ID (code: "invalid_external_id"); endUser or message sent with another kind of key; dryRun or candidate on an end user run; missing or invalid X-End-User-Id; missing erasure id |
401 | Missing, unknown, or revoked bearer token |
403 | Not a product gateway key ("A product gateway credential is required."); workflow not on the key's allowlist, end users disabled, or the creator lost workspace access ("A workspace API key is required.") |
404 | Run not found for this key and end user; site not granted to any allowed workflow (code: "site_not_found"); end user or erasure not found (code: "end_user_not_found") |
409 | An Idempotency-Key reused with a different body, such as another message (code: "idempotency_replay_mismatch"); the end user is being erased (code: "erasing", Retry-After: 5); cancelling a finished run; re-running an erased customer's run; the customer is already signed in (code: "connection_ready") |
429 | Too many new end users for this key (code: "rate_limited", Retry-After: 60) |
503 | The workflow queue is not configured (end user runs are always queued); website connections are unavailable; erasure needs the queue while the customer has runs in flight, or extraction/compaction is still active (code: "erasure_unavailable"). Retry erasure after the memory jobs finish. |
