Skip to content

Handoff Desk

View as Markdown

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.

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 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.
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.

The same flow from your own tools, with a secret key that has the sessions:write scope:

نافذة الطرفية
# Find work: open handoffs, oldest first
curl "https://api.k-agent.kerneltics.com/v1/handoffs?status=open" \
-H "Authorization: Bearer $KAGENT_API_KEY"
# Reply to the customer on behalf of your team
curl 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 team
curl 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 agent
curl 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.

  • 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), the handoff.unclaimed event fires — and email alerts go out when email is set up on the server.
  • When it expires, the customer gets your expire_message and the agent answers again; with on_expire: "close" the conversation closes instead.

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.

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.