Skip to content
Docs
opendesk.shOpen app

Assistant (Ollie)

Conversations with Ollie, streamed runs, approvals and tips.

Ollie’s routes are for signed-in people only: an API key gets 403. They need AI on the instance and in the workspace, and settings.ai.assistant on (403 assistant_off). The assistant’s display name comes from assistant_name in GET /v1/ai/config.

Method Path Min role Typical cost Description
GET /v1/assistant/sessions viewer $0 Your conversations, newest first
POST /v1/assistant/sessions viewer $0 Start a conversation
GET /v1/assistant/sessions/{id} viewer $0 Transcript and any pending approval
PATCH /v1/assistant/sessions/{id} viewer $0 Rename
DELETE /v1/assistant/sessions/{id} viewer $0 Delete
POST /v1/assistant/sessions/{id}/messages viewer $0.02 per model turn Send a message and run Ollie
POST /v1/assistant/actions/{id}/decision viewer $0.02 per model turn Approve or deny a proposed change
GET /v1/assistant/tips viewer $0 Short tips from queue counts

On opendesk Cloud each model turn is charged to the AI balance on its real cost, on the workspace’s model. The typical cost column is what a turn is charged when that cost is unknown; costs.assistant in GET /v1/ai/config is a typical turn on the workspace’s model. On the workspace’s own provider key it is $0.

Conversations are private to their user: another user’s id answers 404.

POST /v1/assistant/sessions/{id}/messages
Accept: text/event-stream
{ "text": "Who should take this?", "context": { "page": "ticket", "ticket_id": "0190…" } }

context describes the page the user is on (path, page, folder, view_id, ticket_id, filters), so “this” means the open ticket. Text is 1 to 8,000 characters.

With Accept: text/event-stream the run streams:

Event Data
session The conversation (its title is set from the first message)
text { "text": "…" }, a piece of Ollie’s markdown answer
tool A step: { id, tool, label, status, summary?, refs?, action_id? }, sent when it starts and when it ends
approval { action_id, call_id, tool, preview, token, expires_at }
done { "status": "complete" } or { "status": "awaiting_approval" }
error A problem body; the stream ends

Without it, the same run comes back as one JSON object: { session, text, steps, approval, status }.

A step’s status is running, done, error, awaiting_approval, approved, denied or expired. refs link tickets (kind: "ticket", with number), contacts, articles, views and automations.

Errors: 402 ai_balance_empty before anything is stored, 409 assistant_busy while the conversation is already running, 429 past 30 messages a minute, 501 ai_unsupported when the provider has no tool use.

Ollie never changes anything on its own. A change arrives as an approval event with a preview:

{
"title": "Update #42",
"summary": "Refund for duplicate charge",
"changes": [{ "field": "Assignee", "from": "Unassigned", "to": "Alex Morgan" }],
"targets": [{ "kind": "ticket", "id": "0190…", "label": "Refund for duplicate charge", "number": 42 }],
"external": false
}

body holds the markdown of a reply or note, total the number of matching tickets for a bulk change, and external: true marks a reply that reaches the customer.

POST /v1/assistant/actions/{action_id}/decision
Accept: text/event-stream
{ "decision": "approve", "token": "<approval token>" }

The token is bound to the action, the user, the sign-in session and the workspace, and lasts 15 minutes. A decision runs once: a second answers 409 approval_not_pending, a wrong token 403 approval_invalid. Sending a new message expires the pending change. The response is a run, as above, in which Ollie continues.

An approved change goes through the regular API with the user’s permissions. Ticket activities record it with actor_type: "ai" and the user in actor.user, shown as Ollie, and the audit log records assistant.action.

{ "tips": [{ "key": "sla_due_soon", "text": "3 tickets breach SLA within the hour.", "prompt": "Which tickets breach SLA in the next hour?", "severity": "high", "count": 3 }] }

Tips are computed from counts without the model and cost nothing. Keys: sla_breached, sla_due_soon, sla_at_risk (open tickets due within 4 hours whose stored triage sla_risk is 0.6 or more), unassigned_old, mine_waiting.

Edit this page on GitHub