- Before the handoff, your code picks which turns of the bot’s chat to send. The default is the whole current session.
- After the handoff, everything your customer sees goes into the Hinto thread: the customer’s messages and every message your bot shows them.
In the browser
This path uses only@gethinto/browser-sdk. It needs the same setup as Hinto Signal: the publishable key and an authenticated token endpoint at /api/support-token. The handoff keeps the customer’s browser session, so investigations can cite what the customer did.
Connect with your bot’s name
Connect once per signed-in customer. The name you pass is the name your team sees. A name with nothing left after cleaning throwsTypeError("invalid_bot_name").
Hand off
The customer’s own words open the ticket, so put their last message in the draft. Then send with ahandoff. The reason is required. The summary and transcript are optional.
- It starts the transcript at
startFromTurnIdwhen you pass one. An id it can’t find keeps every turn. - It drops blank turns and keeps the newest 100.
- It cuts each turn to 4,000 characters, the reason to 500, and the summary to 4,000.
- It tells your team how many earlier turns it left out.
send({ handoff }) answers like any other send. A blank reason answers invalid_draft. After the last conversation resolved, it starts a new conversation. While a conversation is still open, it sends a plain message and drops the handoff.
Let your model decide
If your bot uses a language model, give it a tool. The model decides when to hand off and writes the reason and summary. Your code still decides which turns go along.execute, and call this from onToolCall.
Mirror what your bot says
CallshareBotMessage for every message your bot shows the customer. Your team then never contradicts something the bot already said. Each message is recorded once per id, so a retry or a reload never adds it twice.
A message shared before the runtime first reads the conversation, such as right after a reload, answers
queued. It goes out once the read finds an open conversation, and it is dropped when the read finds none.
Call it right after send({ handoff }) for the bot’s own “I’m passing you to the team” message. It waits for the new conversation, then appears after the customer’s message. A bot message that fails never holds back the customer’s messages.
Send the customer’s messages to your team
While your team has the conversation, the customer’s messages go to them throughsend(), not to your bot.
Show replies and status, and give control back
Your chat shows your team’s replies and the status from the snapshot. Teammate names stay hidden from the customer.snapshot.current.state is none before the first handoff, active while your team has the conversation, and resolved once they close it; then your bot answers again.
isSupportReply(message) from @gethinto/browser-sdk/conversation: the automatic reply has origin: "automatic".
Handle sign-out, errors, and help offers
- Sign-out and account switches. Call
support.updateAuth({ subject, getToken })when another customer signs in, andsupport.updateAuth(null)at sign-out. The runtime moves capture to that customer, so do not callidentifyyourself. Callsupport.destroy()when your chat unmounts for good. - What the snapshot can be.
snapshot.stateisidle,loading,ready,sending,offline,retryable_failure,auth_required,error, orclosed. Tell the customer when support isofflineor failed and callsupport.retry(), and send them to sign in onauth_required. A send answersbusywhile a help offer’s message is on its way. While a send issending,offline, orretryable_failure, its message issnapshot.pendingTextandsnapshot.draftno longer holds it, so empty your input as soon as you callsend. - What a call answers.
send,open, andretryanswerok, or a reason:invalid_draft(nothing to send),attachments_not_ready,busy,requires_start_new(callstartNewfirst),unavailable,auth_required,error, ordestroyed. - Help offers. Your chat can show Hinto Signal’s help offer next to the step where a customer is stuck, through
support.offers. See Offer help without the React kit.
From your server
For a bot with no web page, your server talks to Hinto directly. It hands off, sends the end user’s messages, mirrors your bot’s messages, and reads your team’s replies. These requests carry no browser session, so the ticket cites no session evidence.Create a secret key
Your server needs two values:
The secret key is shown once. Keep it on your server only. Never put it in a website or an app, and never use the publishable key here. You can revoke it at any time in Settings > API keys.
The endpoints
Every path starts with/v1/native-support/server/apps/{appId}/users/{userId}. appId is your Signal app ID, HINTO_SIGNAL_APP_ID. userId is your own stable ID for the end user, the same value a browser token sends as sub.
Send an
Idempotency-Key header with a UUID on both POSTs that carry a customer message. Store the UUID with the end user’s message, so a retry sends the same key and never adds a second message. A retry with the same key and a different body answers 409 idempotency_conflict. A bot message is idempotent on its id instead.
A missing or unknown key answers 401. A publishable key, or an older key made for another use, answers 403. An app ID that is not your organization’s Signal app answers 404, and a paused app answers 403 application_disabled. Each key may send 600 requests a minute, and each end user 120.
Hand off
The first request creates or updates the end user fromuser and opens the conversation. A user field you leave out keeps its stored value, and null clears it.
The handoff takes botName, reason, summary, and transcript. Hinto fits it the way the browser runtime does: it drops blank turns, keeps the newest 100, clips each to 4,000 characters, and counts the turns it left out. A turn’s at that does not parse is dropped. A blank reason answers 400 invalid_message.
Send the end user’s messages
Mirror your bot’s messages
Read your team’s replies
To hear about a reply without polling, set a reply webhook (see Reply webhook). Otherwise poll the conversation, and waitconversation.status.refreshAfterMs milliseconds between polls, or 15 seconds when there is no status. Every poll returns the whole conversation. Send each support message to the end user once, by its id. When state is resolved, your bot answers again, and the next handoff opens a new conversation.
A complete server bot
The same four requests in TypeScript, withfetch:
Check your work
- Talk to your bot, then hand off. The ticket appears in your Hinto Inbox with the handoff at the top, and the earlier chat collapsed under it.
- Let your bot say something after the handoff. It appears in the thread under your bot’s name.
- Reply from the Inbox. The reply appears in your chat.
- Resolve the ticket. Your bot answers again, and the next handoff opens a new ticket.
Reply webhook
Hinto can call your backend when a teammate replies, so you can send a push or wake your bot. Set the URL in Settings > Hinto Signal, under Reply notifications. It must be a publichttps URL. The webhook has its own signing secret. Hinto shows it once, when you first save the URL and on each rotation; a rotation keeps the previous secret signing for 24 hours.
After a reply is delivered, Hinto sends one POST with content-type: application/json:
sub is the subject your backend signed into the user’s token, and preview is the reply’s first 140 characters. A test send from Settings carries "test": true.
Verify each request before you act on it. The Hinto-Signature header reads t=<unix seconds>,v1=<hex>, where the hex is HMAC SHA-256 of <t>.<raw body> keyed with your secret. During a rotation it carries one v1 per live secret: accept the event when any of them matches, and refuse a t more than 5 minutes old. Answer with any 2xx. Hinto retries a 429, a 5xx or a network error up to six times over about 51 minutes, so deduplicate by id (also in the Hinto-Event-Id header). Settings lists recent deliveries with their status codes.