@get-asset/react

Chat

Styled, drop-in chat with the bookkeeping agent — a card holding a titled header over a scrolling transcript, above a composer pinned to the bottom. Owner messages sit right as bubbles; replies read as prose on the left, with their source, follow-up prompts, and feedback controls stacked underneath. Replies stream in token by token and the thread keeps itself scrolled to the newest one. Enter sends, Shift+Enter adds a newline, and the send button becomes a stop button while a reply is generating. Covers loading, error, empty, and success.

Preview

Loading preview…

Required scopes

The access token used by AssetProvider must include these scopes for this primitive to fetch data successfully:

  • chat:create
  • chat:read
  • chat:update

API reference

Root props

PropTypeDefaultDescription
chatIdstringAn existing conversation to continue. Its transcript is fetched on mount, and swapping it for another id opens that one in place. Omit to start fresh — the chat is created on the first message.
promptstringSend a message supplied by another input. Each non-empty prompt is sent once; clear the prop before sending the same prompt again.
greetingstring"Good afternoon, {business}"Heading shown above an empty transcript. Defaults to a time-of-day greeting addressed to the business.
subtitlestringSentence under the greeting explaining what the agent can answer.
thinkingstring"Reading your books…"Line shown beside the animated dots while a reply is being composed.
placeholderstring"Ask anything about your business…"Composer placeholder.
disclaimerstring"Answers are generated from your books…"Small print under the composer, once a conversation has started.
titlestringThe conversation's title, shown in the header. Defaults to the opening question; pass a string to own it yourself.
historyChatHistoryEntry[]Past conversations for the side panel. Fetched from the API by default; pass [] to skip the request.
defaultHistoryOpenbooleanfalseWhether the history panel starts open.
onSelectChat(chatId: string) => voidCalled when a past conversation is picked. It opens in place, so this is a notification — useful for mirroring the choice into a URL.
onBack() => voidNotified when the header's back arrow is used. Back already returns to the resting view on its own; supply this to also navigate or update a URL.
onExport(messages: ChatMessage[], title: string) => voidCalled by the header's download control. Defaults to downloading the transcript as markdown.
onGraphExport(graph: ChatGraph) => voidA headless hook for exporting a graph's data (CSV by default via the SDK's GraphExport slot). AssetChat's own download control exports the whole graph card as an SVG image instead, so it doesn't call this.
logoReactNodeMark shown above the greeting on the resting view — typically the business's logo.
onAttach() => voidProvide to show an attach control in the composer. The chat API accepts text only, so the upload is yours to own.
resumebooleantrueRejoin a turn that was still streaming when the page reloaded, or one still running in a conversation opened from the history panel.
onChatCreated(chatId: string) => voidCalled with the new chat's id the first time a chat is created — persist it to reopen the conversation later.
onFinish(message: ChatMessage) => voidCalled with the finished assistant message at the end of each turn.
messagesChatMessage[]Override the transcript (skips the API call).
state"loading" | "error" | "empty" | "success" | "thinking" | "streaming" | "turn-error"Force a specific view for testing.
classNamestringForwarded to the outer Card wrapper element.

States

LoadingHydrating an existing conversation.

Loading preview…

Error

Loading preview…

EmptyThe greeting and the composer, before the first message.

Loading preview…

SuccessAn answered question — the reply's source line, follow-ups, and copy control.

Loading preview…

With graphsA question answered with graph cards as well as prose — a bar chart comparing categories and a line chart tracking a trend, each with its summary, legend, and provenance line.

Loading preview…

With a forecastA forward-looking answer carrying a cash projection — booked history solid, the projection dashed, the likely range shaded, and the readiness and driver assumptions behind “See details”.

Loading preview…

ThinkingThe turn is running but no reply text has arrived — the agent is typically calling a report tool. Send swaps to Stop.

Loading preview…

StreamingReply text arriving token by token.

Loading preview…

Turn errorThe turn failed; Try again resends the same message.

Loading preview…

Anatomy of a reply

Replies are markdown — paragraphs, bold section leads, bullet lists, and inline code, which is how figures reach the page (`$63,880` renders as a value rather than prose). Anything outside that set falls through as plain text.

Everything else around the answer is optional. A sourceline under the answer comes from the reply’s metadata; the follow-up prompts beneath it come from its suggestions, and clicking one sends it as the next question.

Graphs

For a comparison question the agent may answer with a graph as well as prose. It renders as a card under the answer: the title and summary, the chart with its legend, and the provenance line beneath. A bar graph compares categories (a label above each bar); a linegraph tracks each series across an ordered axis. The card picks the shape from the graph’s type — you drop in one AssetChat either way.

Graphs are persisted on the reply, so a conversation reopened from history draws them again. The download beside the title saves the whole card as an SVG image — title, chart, legend, and footnote, composed from the live layout with the theme’s colours inlined so it stands alone. Colours come from the --asset-chart-series-*tokens — a fill and outline per series for bars, a saturated linestroke for lines, assigned by recency so the most recent series leads in purple (with the area gradient on lines) and earlier ones fall to the muted colours — so a chart takes the host’s palette like everything else.

Forecasts

A forward-looking answer may carry a forecast card. A projection charts one scenario over time: booked history draws as a solid line, the projection continues it dashed from “today”, and the P10–P90 likely range is shaded behind — for a cash forecast a $0 line marks the floor to watch. A comparisondraws a line per scenario over the projected periods only, the base one emphasised — no history/projection split, since scenarios are identical over the past. The card picks its shape from the forecast’s type; you drop in one AssetChat either way.

Beneath the chart, “See details” reveals the forecast’s readiness— a confidence badge, a plain-English assessment, and, when the agent needs input, tappable question chips that send an answer back into the forecast — and its drivers, the assumptions the number was built from, formatted by kind (a ratio as a percentage, days as days, the rest as amounts). The download beside the title saves the whole card as an SVG, the same as a graph.

The header holds a back arrow, the conversation’s title, a download, and the history toggle. Every part bar the toggle renders only once there’s a conversation, so the resting view keeps the bare toggle. The back arrow returns to that resting view — a fresh conversation — with no wiring; pass onBack only if you also want to navigate or update a URL. The title is read-only.

Sizing

The chat is a Cardlike every other component, so it takes the same border, radius, and background from the theme. Unlike the others it fills the height it is given and sits at that height throughout — the resting view is already full height, so opening a conversation fills the transcript in and scrolls rather than resizing the card. Give the parent a height (a fixed-height box, a flex child, or a grid cell); with no height to fill it falls back to a 420px minimum and grows with its content.

Mobile

When the chat is wide the history panel sits beside the conversation. Once it narrows past 720px — a small screen, or the component dropped into a narrow column — opening history takes over the whole card as an overlay instead of a cramped side panel, with its own close button (the header’s history toggle is covered while it’s open). It’s a container query on the chat’s own width, not the viewport, so a narrow embed on a wide page behaves the same as a phone.