Option A: hosted chat with DGI
The hosted chat page already draws every card, the card toolbar, row actions and the / palette. You configure it with workflows only. Structured chat is part of DGI and is Alpha.
Every step below is started by a person in your organization. The responder and reply-principal steps need an organization admin or owner, signed in as themselves. Agents and API keys are refused, so an assistant can prepare each call for you, but you start it.
1. Enable the chat space and publish the page
If your identity app has no chat yet, follow Enable and publish first. In one call:
start_workflow("buzz.space.enable", {
"identity_app_uuid": "<your identity app uuid>",
"publish_chat": true
})
publish_chat: true publishes the hosted chat page at the end. Keep the space_uuid from data.buzz.space.get for the next steps.
2. Bind a Staff actor to a paid seat
The actor signs every answer with its own chat key, so it needs an end-user seat in the app, like any member. Hire the actor first, then:
start_workflow("identity.end-user.bind-actor", {
"identity_app_uuid": "<your identity app uuid>",
"staff_actor_uuid": "<actor uuid>",
"reason": "Support assistant in the app chat"
})
This takes one paid end-user seat. Check it with identity.end-user.actor-binding.get.
3. Attach the actor to the space
start_workflow("buzz.actor.attach", {
"space_uuid": "<space uuid>",
"staff_actor_uuid": "<actor uuid>",
"display_name": "Support",
"triggers": {"mention": true, "direct_message": true},
"max_replies_per_hour": 60
})
A dgi responder does not need the actor to declare that it acts as the app, because DGI never runs as the actor's seat. A hybrid responder does, because a handed-off turn runs as the seat. buzz.actor.attach itself still asks for the declaration unless you attach in organization mode (step 5). Details in Actors in chat.
4. Set the DGI responder
Get the attachment_uuid from data.buzz.attachment.list, then:
start_workflow("buzz.actor.set-responder", {
"space_uuid": "<space uuid>",
"attachment_uuid": "<attachment uuid>",
"responder": "dgi",
"allowed_workflow_types": ["<a read your app exposes>", "<a write your app exposes>"],
"system_prompt": "You are the support assistant of Acme. Answer in the customer's language."
})
allowed_workflow_types is required: 1 to 50 exact names, or prefix.*. DGI can start only what is on this list and what the person who wrote the message may run. Every other setting is optional; see the responder reference.
You do not need buzz.bridge.sync after this step: the bridge does not read the responder.
5. Choose who the replies run as
| Reply principal | DGI runs as | Can reach | Use for |
|---|---|---|---|
seat (default) | The end user who wrote the message | Workflows your app exposes to end users, intersected with allowed_workflow_types | Customer-facing chat |
organization | The author's linked organization account, or else the admin who turned internal mode on | Workflows of your organization, limited to allowed_workflow_types | An internal space where everyone is on your team |
Seat mode is the default, so a customer-facing actor needs nothing here. For an internal support space:
start_workflow("buzz.actor.set-reply-principal", {
"space_uuid": "<space uuid>",
"attachment_uuid": "<attachment uuid>",
"reply_principal": "organization",
"allowed_author_end_user_uuids": ["<end user uuid>", "<end user uuid>"]
})
Organization mode is available only in organizations Orkestia has enabled it for, and it answers only listed people in a space where every person is listed. Read Internal support actor before you turn it on.
6. Sync the bridge
start_workflow("buzz.bridge.sync", {"space_uuid": "<space uuid>"})
Run it after every attach, pause, resume or detach. It starts the bridge that delivers messages to the actor.
7. Try it
Open the chat page, mention the actor or open a DM with it, and ask for something one of its workflows does. A read comes back as a table, chart, tiles or logs. A write comes back as a form, then a confirm card. Type / in the composer to see its commands.
After a platform upgrade
New chat features arrive in two places: new entry points on your app, and a new version of the hosted page.
- Entry points. Re-run
buzz.space.enablefor the app. It is safe to re-run: it runsbuzz.space.publish-entrypointsfor you, which adds only the entry points the space does not have yet (for examplecommands, which the/palette needs) and republishes the ones whose inputs changed.buzz.space.publish-entrypointsis internal and cannot be started on its own. - The page. Publish it again:
start_workflow("buzz.space.publish-chat", {"space_uuid": "<space uuid>", "also_site_slug": true})
buzz.space.publish-chat publishes the page on the chat's own address and returns it as url. With also_site_slug: true it also publishes the same page on your site's https://<slug>.app.orkestia.dev, replacing what that address served, and returns it as slug_url. If your people open the chat on the slug address, pass also_site_slug: true every time, or they keep the old page.
Worked example: a Support actor over tickets
Acme runs an internal support space for its own team, so the actor replies in organization mode (step 5) and uses the organization's Tickets workflows. A customer-facing actor in seat mode would list the workflows the app exposes to end users instead, because ticket.* workflows are not available to end users.
{
"space_uuid": "<space uuid>",
"attachment_uuid": "<attachment uuid>",
"responder": "dgi",
"allowed_workflow_types": ["ticket.search", "ticket.get", "ticket.open", "ticket.comment", "ticket.assign"],
"system_prompt": "You are Acme's support desk. Keep answers short. Always show tickets as a table.",
"decision_engine": "auto",
"default_inputs": {
"ticket.search": {
"labels": ["support"],
"status": ["open", "triaged", "in_progress", "blocked"]
}
},
"suggestions": [
{"label": "Open tickets", "prompt": "List the open support tickets"},
{"label": "Blocked tickets", "prompt": "List the support tickets that are blocked"},
{"label": "New ticket", "prompt": "Open a new support ticket"}
],
"max_reasoning_turns": 12
}
What happens in the chat:
- "Which tickets are open?" DGI runs
ticket.searchwith the default filters (labelsupport, the four active statuses) and posts a table. The table offers Refresh, Live, and row actions forticket.get,ticket.commentandticket.assign, because each needs exactly the row's ticket id. - "Show me the blocked ones." The message names a value of a defaulted filter, so DGI narrows
statustoblockedand says so in the title. - "Open a ticket about the login page." DGI answers with a
ticket.openform, prefilled with what it could read from the message. After Submit, it posts a confirm card. The ticket is opened only when the person presses Confirm. - The quick replies "Open tickets", "Blocked tickets" and "New ticket" appear under answers. Tapping one posts the label as the person's message.
Ask your AI assistant
Walk me through turning on a DGI responder for my chat actor "<actor name>". Check the seat with identity.end-user.actor-binding.get, find the attachment with data.buzz.attachment.list, and show me each call before I start it.
Draft a buzz.actor.set-responder configuration for a support actor that can search and open tickets, with default filters for open tickets and three quick replies. Do not start it.
My chat page does not show the / command palette. Check whether my space has the commands entry point and tell me what to re-run.
Republish my chat page with buzz.space.publish-chat and also_site_slug true, after I confirm.
For AI agents
| Rule | Detail |
|---|---|
| Order | buzz.space.enable, identity.end-user.bind-actor, buzz.actor.attach, buzz.actor.set-responder, optionally buzz.actor.set-reply-principal, then buzz.bridge.sync |
| Human callers | Every step refuses agents. Prepare exact calls; a person starts them. set-responder and set-reply-principal need an admin or owner |
| Scope first | Before proposing allowed_workflow_types, read each type's schema and check end_user_eligible for seat mode. A type the person cannot run is never offered, whatever the list says |
| Upgrades | Re-run buzz.space.enable for new entry points, then buzz.space.publish-chat, with also_site_slug: true when people use the slug address |
| Revert | buzz.actor.set-responder with responder: "staff" removes the DGI entry. Nothing else changes |
Structured chat with DGI
Let DGI answer for a chat actor, or for any app over an API, with forms, confirm cards, tables with row actions, charts, KPI tiles, data grids, diagrams, live cards, quick replies and a / command palette, built on your organization's own workflows. Part of DGI (Alpha)
Option B: embed the chat component
Put the Orkestia chat, with every structured card built in, inside your own React app. Install, sign the person in, bootstrap the chat, mount BuzzChat with loadCommands and uiRenderers, and theme it
