What lands in Call History
Every screen, field, tap, friction signal, action and identity the SDK reports ends up on the call record — here is exactly where each one shows up, and what it looks like.
Nothing needs instrumenting beyond what the screens already report. Each thing the SDK sends has one place it lands in Call History, on the session's panel and in the CSV export.
The mapping
| You call | The agent receives | Call History shows |
|---|---|---|
useGraineScreen / useGraineField | app_context — screen, fields, errors | What happened in the app · "opened privacy", "moved to favourites (from privacy)" |
useGraineTap / useGraineTrack | recent_events on the next context frame | What happened in the app · "tapped pay", "toggle_changed" |
| Friction rules (configure) | app_event with the sentence the agent hears | What happened in the app · "stalled on step: agent spoke up" / "rage taps: agent stayed quiet — it had spoken up moments before" |
client.reportEvent(...) | app_event | What happened in the app · a signal row, same shape |
useGraineAction handler results | the tool result the model reads | What the agent did in the app · action, arguments, ✓ / refused / timeout, and your handler's own message |
identify() | prompt variables + app_user; the identifier only identifies the customer | who it was with on the row and the panel header — the identifier as label · value, else the name (below) |
appVersion on the provider | app_version on the context frame | the record, for telling a build apart |
| Nothing — the SDK reports it | app_event flagged telemetry | What happened in the app · ⇅ rows: "transport: WebRTC", "connection quality: poor · rtt 480ms", "network: offline", "transport: fell back to WebSocket (media)". Never spoken by the agent |
The panel also carries the transcript (both sides, WebRTC calls included), Result (Completed for a web call), Talk Time (the conversation's length; a web call has no carrier duration), and cost.
Who it was with
Each row and its panel name the customer by the identifier your app sent, with the label of the agent's identity setting: User ID · u_8812, Email · asha@acme.example, Customer ID · C-20931. Without an identifier the row shows the customer's name, from their traits, and failing that the connection's id. A phone call shows Phone · the caller's number; a call Graine rang shows the customer it was booked for. Search Call History by an identifier to find a customer's conversations.
Graine stores the identifier your app sends so your team can see who each conversation was with; the memory service only ever receives a one-way key derived from it — Identity and users.
Why a signal says the agent stayed quiet
A friction signal is a request, not a command. The agent stays quiet while
it is already talking, inside nudge.cooldownSeconds of its last
intervention, and past nudge.maxPerSession. The timeline records which —
it was mid-sentence, it had just spoken up, it had reached its limit —
so a product team can tell "the agent ignored a stuck customer" from "the
agent was already helping".
CSV
Web-call exports carry Contact Name, App Actions (one line per action,
with the app's reply) and App Timeline (one line per entry, oldest first),
beside the columns every call has.
What is not there
Only what the app reported. A screen that never called useGraineScreen
appears by route name only; a tap not wrapped in useGraineTap is not on the
timeline. Calls made before this was stored show a transcript and a status
and nothing of the above.

