Apps and end users
App tabs, how an app run routes to an agent, and how anonymous and signed-in end users are identified.
An app is what you deploy. Create one under Apps, attach agents, configure who may use it, install the widget, and monitor its conversations and usage.
#App tabs
- Overview: name, description, environment, system prompt for the app.
- Auth context: the identity policy and the user fields agents may see. Enforcement is either allow anonymous (visitors can chat without signing in) or require verified (the widget shows a sign-in gate until your site identifies the user). Custom claim fields declare which values from the identity token exist and whether they enter agent prompts.
- Credentials: the public app id (safe to ship in a web page) and the server-side app secret (never ship it to a browser). The secret authenticates your backend's calls and signs end-user identity tokens. It can be revealed once and rotated; rotating it invalidates the previous secret immediately.
- End users: the people who have used the app, with their sessions. Sessions can be revoked.
- Install: the widget snippet with your values, the chat UI settings (placeholder, welcome message, suggested prompts, accent colour, theme, position, launcher label, branding, and whether built-in tool activity is shown) and a live preview.
- Workflow: the app graph on the canvas — the chat entry (with the input safety rules), the Agent nodes and Chat (out), the finish node that screens the answer with the output safety rules and ends the run — with versions, validation and publishing like an agent graph. What you see is what runs. This is where an app gets its agents: add an Agent node and pick one of the organisation's agents in it. One agent needs no Router; several agents sit behind a Router that routes to each Agent node. A published version that real runs used is read-only; duplicate it to edit.
- Test: chat with the app as a builder.
- Server runs (2026-09-23): any published workflow accepts runs from your backend
with the app secret; they enter through Chat (in) like a chat turn:
POST /api/app/server/runswith a text or structured input, an optionalthread_keythat keeps one thread across steps,mode: syncto wait for the answer or async with acallback_url(delivered by a Webhook (out) node with target callback_url) or polling, anidempotency_keyso retries never run twice, and anidentity_tokenorauth_contextwhen a person is behind the task. Acallback_urlmust be a public https address (Guardrails, approvals and safety). Every refusal is named (for exampleAPP_GRAPH_NOT_ACTIVE,INPUT_INVALID,CALLBACK_NOT_WIRED). The Credentials tab shows ready-made curl calls. - Slack (2026-09-23): a bot user anyone in a workspace can DM or mention. Create
your own Slack app from the manifest on the Integrations page (so the bot carries your
name and icon), install it, paste its Bot User OAuth Token and Signing Secret under
Integrations → Slack workspaces, and give Slack the receiver URL from the app's
Credentials tab. Put Slack (in) before Chat (in) and Slack (out) after Chat
(out), both picking that integration, and publish. A DM is one ongoing conversation
per person; a mention in a channel is answered in that message's thread. Every Slack
thread is a conversation, so it shows in the Inbox and the support hand-off works over
Slack (the team's replies reach Slack in a later phase). If the agent's tools act with
an end-user access token, paste a tool access token on the workspace too: every
Slack turn then enters with it and those tools bind. It is one identity for everyone
in the workspace; without it the tools stay withheld (the run trace says
no_token) and the agent answers without them. - Inbound webhooks (2026-09-23): put a Webhook (in) node before Chat (in), pick a
webhook integration on it, and other systems start runs by POSTing JSON to
POST /api/webhooks/in/<app_id>signed with that integration's secret (X-AgentRow-Signature: t=<unix>,v1=<hex>, HMAC-SHA256 of<t>.<raw body>; the node's replay window boundst). The node's paths pick the input, the thread key and the identity out of the payload; a repeated event id answers "duplicate" and starts nothing. The Credentials tab shows the receiver URL, a signing example and what the receiver saw — accepted, duplicate or rejected, each with its reason. - Webhook delivery (2026-09-23): connect a Webhook (out) node to Chat (out) and
every finished answer is also POSTed as JSON to a URL of yours — several Webhook (out)
nodes deliver to several URLs. Create a webhook integration under Integrations
(its signing secret is shown once), pick it on the node, and verify each delivery's
X-AgentRow-Signatureheader (t=<unix>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with the secret). Each delivery is its own step in the run trace with the HTTP status; a failed delivery never fails the run or stops the next delivery. Deliveries go to https URLs at public addresses only: a URL atlocalhostor on a private network fails its delivery by name (Guardrails, approvals and safety).
#How an app run works
A message to an app runs the app graph: the turn enters through the chat channel node
(chat_in), which applies the app's input safety rules, the Router selects an agent
when there are several, the selected agent's published workflow runs as a nested run,
and the app's finish node
(Chat (out)) streams only the selected agent's final answer and applies the app's output
safety rules to it. Nested runs are logged with the
same detail as agent runs.
#App types
An app is created as conversational (chat through the widget or your own UI), support (conversational plus the support surfaces: the hand-off node and the inbox, Slack threads included; WhatsApp and email to come) or task (no chat surface; started by your server or a signed webhook, answering in place or posting the result back through a Webhook (out) node). The type gates the surfaces the app gets. You can change it at any time on the app's Overview tab: pick another type and the page lists what the change affects before you save. A change that would break the live app is blocked with the fix named (a published Support hand-off node must be removed before an app stops being a support app). Consequences need your confirmation: a task app has no chat surface, so every installed widget stops working; leaving the support type makes the inbox read-only and returns the team's conversations to the assistant. Earlier runs, logs and conversations are never rewritten. Channels are nodes on the app graph: Chat (in) and Chat (out), Slack (in) and Slack (out), Webhook (in) and Webhook (out), and the Shopify store, which attaches to Chat (in) (Shopify store assistant).
#Support hand-off (support apps)
On a support app, attach a Support hand-off node to an Agent node on the Workflow
tab (drag from the small square port under the Agent node). The agent then has a
built-in handoff_to_human tool and knows when to use it: when the user asks for a
person, when a decision needs a human, or when it cannot resolve the request — or
whatever you write in the node's first prompt, When to hand off. The node's second
prompt, How to reply after the hand-off, is given to the agent as written: "Reply
with exactly: …" makes every hand-over say the same sentence; "in your own words, say
what you could not answer and that the team will reply here" makes each one fit the
conversation. The moment the agent calls the hand-off, the conversation moves to your
team, the assistant replies as your prompt says (if it says nothing at all, the reply is
"I've handed this conversation to the
#Inboxes
Inboxes in the sidebar lists every published app. Open one to read that app's conversations: the list is on the left and the selected conversation opens on the right. Sessions you ran yourself from the app's Test tab are marked test.
- Support apps have Open, Resolved and All tabs. Open holds the conversations the assistant handed to your team; the right pane shows the agent's reason and summary and lets you reply as the team and resolve. Conversations the assistant is still handling appear under All and are read-only until it hands them over.
- Every other app has the All tab only, and its inbox is read-only.
The same inbox is also on the app page as the Inbox tab.
#End users and identity
- Anonymous visitors get a server-issued anonymous id on first open; the widget keeps it so the visitor's conversations persist across visits.
- Signed-in users are identified with an identity JWT your backend signs with the
app secret (HS256). Required claims:
sub(your user id) andexp(expiry). Optional:email,name, atool_tokenfor authenticated tools, and custom claims declared in the Auth context tab (snake_case names, scalar values). Invalid tokens are rejected with explicit error codes; they never silently become anonymous sessions. - If a visitor chatted anonymously and then signs in, the anonymous history is merged into the signed-in user.
- Sessions use an access token and a refresh token stored in the browser; the widget refreshes them automatically and reports when a session ends.
Updated 30 September 2026