Requirements
@gethinto/browser-sdk0.5.0 or later for journeys. Help offers need@gethinto/browser-sdk0.8.0 or later, and@gethinto/native-support-react0.5.1 or later for the React slot.- Capture that knows the signed-in customer, through
initialIdentityorbindConversationIdentitywith{ kind: "external_id", value: user.id }. Hinto Signal sets this up. Without a signed-in customer,journey()returns a handle that does nothing and, withdebug: true, logs why.
Report a journey
Calljourney(key) where the flow begins. Call attempt(action) in the submit handler, before the request. Call fail(...) in the error branch you already have. Call succeed() only after your server confirms the result, never on click.
journey(key) is idempotent while the journey is open. It returns the open instance for that key in this tab, or starts a new one. The instance lasts through reloads and redirects in the same tab, so a flow that leaves for an OAuth provider and comes back is still one journey. After succeed(), the next journey(key) starts a new instance. Two tabs track two instances.
When a submit control can be disabled
A disabled button emits no click, so the customer can try to submit and nothing happens. If your form disables its submit control while it is invalid, report the attempt from a wrapper that sees the attempt, and say how the customer was told:feedback: "inline": the error appears next to the field or the control.feedback: "summary": the error appears in a banner, toast, or summary at the top.feedback: "none": the customer saw nothing.
Offer help on the journey
Reporting a journey alone shows nothing to the customer. When the journey’s events suggest the customer is stuck, for example after repeated failures, Hinto Signal decides whether to offer help. The offer appears only where your product placed a slot for it. Without a slot, Hinto still records the decision, but the customer never sees it. With the React kit, place<Support.HelpOffer> next to the step the journey covers, under your Support.Provider:
journeyis the same key you pass tojourney(key), or an array of keys.topicnames the step in the customer’s words. It fills the card’s title and the message that Contact support sends.onRetryis the step’s existing handler. Without it, the card has no Try again button.- Keep the slot mounted. Do not wrap it in a condition. It renders nothing until support has started for a signed-in customer and there is an offer, and it shows the offer only while it is on screen.
- Place one slot per journey on a screen, on the screen where the customer fails. A slot on a different page never shows the offer.
onRetry. Not now hides the offer. Nothing is sent to your team until the customer chooses Contact support.
The slot sits on your product page, outside the conversation. Theme tokens you set on the conversation’s class do not reach it. Set them on :root or on a class you pass as the slot’s className, and pass theme="dark" in dark mode. Customization lists the tokens.
Hinto decides when to offer help. Repeated failures, a missing prerequisite or permission, a rejected input, or a failure the customer never saw can bring an offer. Your team controls it in Hinto under Settings > Hinto Signal: Proactive help turns offers on or off for each journey, and Offer limits sets how long an offer stays, how long to wait between offers, and how many a customer sees in a week. A customer who chooses Not now is not offered help on that journey again for the quiet period.
Offer help without the React kit
With@gethinto/browser-sdk/conversation alone, for example inside your own chat, read runtime.offers and draw the offer yourself:
offers.subscribe(listener)andoffers.snapshot()give the current offer. Signal checks for one only while something is subscribed, the page is visible and online, and the customer has an open journey.- When the snapshot’s
statusis"eligible", calloffers.show(id)and draw the offer only when it returns"ok". That records the impression."closed"means the offer is gone, and"unavailable"means the call did not complete. After a reload, a"shown"status means draw it again without callingshow."none"means there is no offer. - Not now calls
offers.dismiss(id). - Contact support calls
offers.contact(id, { message })with a sentence in the customer’s words. On"started", callruntime.open(), which sends it. While that message is on its way,contactanswers"unavailable"and a typed send answers"busy". - After
journey.succeed(), callawait offers.refresh()so the offer goes away at once.
Choose the journey
Instrument a flow your product already has. Add calls to its existing handlers, and do not add product behavior, server routes, or test hooks to make it fail. If the flow is a stub, or has no real point where it succeeds or fails, leave it without a journey. Pick one flow first. A good first journey:- happens while the customer is signed in,
- has an explicit success that your server confirms,
- fails in ways customers write to support about.
Vocabulary
Key, action, and category are tokens: lowercase letters, digits, and underscores, starting with a letter, at most 39 characters.stripe_connection, connect, and third_party are tokens. The Inbox shows the key as a label: stripe_connection reads as “Stripe connection”.
Failure categories. Map your errors to one of these:
Feedback is
none, inline, or summary, as described above.
Limits
Metadata is for tokens such as a plan name or a provider, never for customer data. The SDK redacts metadata strings with the same patterns it uses for every payload, and an event whose metadata has a key that looks like a secret, such as
password, api_key, or tokens_used, keeps its step and drops all of its metadata. Journeys never carry free text: a failure has a category, not a message. See Privacy controls.
Check it
Run the flow once, the natural way, withdebug: true in initHinto. You do not need to make it fail. The SDK writes its debug lines with console.warn and a [Hinto] prefix, so they appear as warnings, not errors.
The SDK logs one ingest response: accepted N, duplicates N, rejected N line per batch, and a batch can hold journey events and other capture together. In the browser’s Network tab, find the /v1/ingest request whose body contains the journey events. The journey works when the response line for that batch shows rejected 0. A rejected event is dropped on its own and never blocks other capture, so a nonzero count means a key, action, category, or metadata value broke a rule above.
To check the help offer slot, open the screen where the journey runs and confirm the page has an element with data-hinto-part="help-offer". It is empty until there is an offer. To see the card without making the flow fail, render the slot with the kit’s testing fixture and call controls.presentOffer({ journey }). The package’s AGENT_INSTALL.md describes the fixture.
If the journey reports correctly but a stuck customer never sees an offer, the slot is missing, is on a different screen, or was off screen while the offer was open. Proactive help may also be off for the journey, or the customer may have reached an offer limit.