Handoff Desk
The Handoff Desk is a small inbox in the dashboard where your team picks up conversations the agent hands over. It is deliberately not a full helpdesk: it does the few things a handoff needs, quickly, on desktop and on a phone. Everything it does is also available through the API, so you can build the same flow into your own tools.
Queues
Section titled “Queues”| Queue | Shows |
|---|---|
| Needs a human | Open handoffs nobody has claimed yet — high priority first, then oldest first. |
| Mine | Handoffs you claimed. |
| All open | Every open handoff. |
| Closed | Resolved and expired handoffs. |
| Flagged | Conversations the agent flagged for review with flag_conversation. |
Filter by reason, agent and channel. One-shot handoffs (from ask calls, with no conversation to continue) are kept apart under the One-shot filter, where you can mark them resolved after following up through your own channel.
The Desk refreshes every 10 seconds, and with your permission shows a browser notification and plays a sound when something new needs a human. Badge counts come from GET /v1/handoffs/counts, which returns {unclaimed, mine, open, flagged}.
The conversation view
Section titled “The conversation view”- the full transcript, including short summaries of the actions the agent took;
- the handoff card: reason, the agent’s summary, who requested it (agent, API, policy or a teammate) and, for liability cases, the exact reply the customer saw;
- what you know about the end user: whether they are verified, their
external_id, the channel and their language; - a countdown to when the handoff expires.
Actions
Section titled “Actions”| Action | What happens | API |
|---|---|---|
| Claim | The handoff is yours. First claim wins; others get 409 handoff_already_assigned. Claiming extends the expiry. |
POST /v1/handoffs/{ho}/assign with {"assignee": "me"} |
| Reply | Your message reaches the customer at once — in the widget, on the session stream and in message.created webhooks. It extends the expiry. |
POST /v1/sessions/{session}/messages with {"role": "human_agent", "input": "…"} |
| Internal note | Visible to your team only; never shown to the customer or the AI. It extends the expiry. | Same, with "visibility": "internal" |
| Release to AI | The agent takes over again, aware of your replies. An optional note is given to the agent once, as internal context. | POST /v1/handoffs/{ho}/resolve with {"action": "release", "note": "…"} |
| Close | The handoff and the conversation are closed. A new customer message reopens the conversation. | POST /v1/handoffs/{ho}/resolve with {"action": "close"} |
| Take over | Step into a live AI conversation before the agent asks for help. | POST /v1/sessions/{session}/takeover with {"summary": "…"} |
| Reassign | Admins can move a claimed handoff to someone else. | POST /v1/handoffs/{ho}/assign with {"assignee": "usr_…", "force": true} |
Replying publicly in a conversation the AI is handling counts as a take-over: a handoff is created for you automatically, so the agent stops answering.
Use the API
Section titled “Use the API”The same flow from your own tools, with a secret key that has the sessions:write scope:
# Find work: open handoffs, oldest firstcurl "https://api.k-agent.kerneltics.com/v1/handoffs?status=open" \ -H "Authorization: Bearer $KAGENT_API_KEY"
# Reply to the customer on behalf of your teamcurl https://api.k-agent.kerneltics.com/v1/sessions/sess_01k6rz4p7h2c9m5x8w3t6v1qbg/messages \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"role": "human_agent", "input": "Hello Fahad, this is Noura from customer service. I am checking your order now."}'
# Leave a note for the teamcurl https://api.k-agent.kerneltics.com/v1/sessions/sess_01k6rz4p7h2c9m5x8w3t6v1qbg/messages \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"role": "human_agent", "visibility": "internal", "input": "Photo of the broken bottle received; replacement approved."}'
# Give the conversation back to the agentcurl https://api.k-agent.kerneltics.com/v1/handoffs/ho_01k6rz6c8e0g2j4m6p8r0t2v4x/resolve \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action": "release", "note": "A replacement bottle ships tomorrow; confirm if asked."}'GET /v1/handoffs filters by status, assignee, reason_type, agent and since. To react in real time, subscribe a webhook to handoff.requested and message.created, or keep the session’s event stream open.
Timing and expiry
Section titled “Timing and expiry”- A handoff keeps the AI quiet for
handoff.ttl_hours(default 24). Claiming it and every message from your team push the deadline to at least now plus that window. - If nobody claims it within
handoff.notify.unclaimed_reminder_minutes(default 10), thehandoff.unclaimedevent fires — and email alerts go out when email is set up on the server. - When it expires, the customer gets your
expire_messageand the agent answers again; withon_expire: "close"the conversation closes instead.
Who can use the Desk
Section titled “Who can use the Desk”The support role is made for the Desk: it can read conversations, claim, reply, write notes, release and close, but cannot see agent settings or run traces. Owners, admins, developers and editors can use the Desk too; viewers can only read.
Tickets
Section titled “Tickets”Next to the queues, the Tickets tab lists tickets opened by the agent’s create_ticket tool (TKT-<n>). Update their status with PATCH /v1/tickets/{tkt} (open, pending, closed), and list them with GET /v1/tickets.