Skip to Content
Components

Human handoff

The agent hands a conversation to a person, with what they need to pick it up.

The agent passes a conversation to a person: why it handed off (reason), who has it (assignee, with a people list for Reassign), when the reply is due (secondsLeft counts down and the bar empties, turning orange under five minutes), the summary and facts they need to pick it up, and the message the customer was told (editable in place when you pass onMessageChange). A status row at the foot says where it stands — neutral while it waits, amber when the person can't take it, green once they have.

Preview
Handed off to a personRefund over $500 needs a person
Assigned to
Finding someoneBilling team
Reply due in 14 min15 min target
Sent to Dana
Thanks, Dana. A refund this size needs someone on our billing team, so I've passed your conversation to them. They'll reply here within 15 minutes.
Context passed along

Dana was charged twice for her annual plan on Oct 21 and wants one charge back. The agent confirmed the duplicate, but can't refund more than $500.

Amount$588.00
CustomerDana Lee · Pro since 2023
Order#48213 · charged twice
Handing off to BillingThe agent has stopped replying to Dana

Usage

<!-- include core.css + human-handoff.css -->
<div class="hox panel" data-c="human-handoff">
  <div class="hox-h">
    <span class="hox-ic"><!-- user icon --></span>
    <div class="hox-ht"><b>Handed off to a person</b><span class="muted">Refund over $500 needs a person</span></div>
  </div>
  <!-- who has it, when the reply is due, the context, and the foot row -->
</div>

Properties

NameTypeDescription
reasonstringWhy the agent handed off, like "Refund over $500 needs a person".
assigneeHandoffPerson | nullWho has it, or null while the agent is still finding someone.
finding{ title; detail? }The two lines shown while assignee is null.
peopleHandoffPerson[]The people it can go to instead. Leave it out to hide Reassign.
onReassign(person) => voidCalled when a row is picked in Reassign.
secondsLeftnumberSeconds until the reply is due. Whole minutes; orange under five; overdue at 0 or less.
targetSecondsnumberThe whole window, for the bar and the "15 min target" line. Defaults to 900.
summarystringWhat the person needs to pick it up.
facts{ label; value }[]A few key facts, as label and value rows.
link{ label; href?; onClick?; tip? }A link to the whole conversation.
messagestringWhat the agent told the customer.
messageLabelstringThe label over the message, like "Sent to Dana".
onMessageChange(text) => voidAdds Edit: the message turns editable, and Save calls this with the new text.
editedbooleanAdds "· edited" to the message label.
statusHandoffStatusThe foot row: tone (wait | warn | ok), title, detail? and an optional person.

HandoffPerson is { name; initials?; color?; team?; presence?; tone?: ok \| warn } — name is the key, tone puts a green or orange dot before the presence.

Theming

Works in light and dark (data-theme="dark") and in the Soft style (data-style="soft"). The card sits on a surface; the countdown bar, the presence dot and the foot row take their colours from the --dm-* tokens — amber from the warn tokens under five minutes or when the person is out, green from the ok tokens once they have it. The two columns stack when the card is narrow.

On this page