Call events
Everything that happens inside one call, in the order it happened.
The call's life (call.*), the two voices as the session sees them (user.*, agent.state, agent.transcript), the finished turns with their metrics (turn.*) and the raw metric blocks (metrics.*, whose fields are in metrics.md). The index is events.md.
agent.state#
The agent's state changed, in the session's own words.
| field | type | required | meaning |
|---|---|---|---|
state |
AgentState |
yes | What the agent is doing right now, in the session's own words: warming up, waiting, hearing the caller, generating, or playing audio. |
agent.transcript#
One delta of the reply the agent is giving, never the reply so far: in a voice call one word, as the voice plays it, with the seconds it was aligned to; in a written call one model token. The reply so far is every delta of the same speech_id since the last turn.agent, joined; turn.agent carries the whole reply and closes it. Interim entries are ephemeral.
| field | type | required | meaning |
|---|---|---|---|
speech_id |
string |
yes | The reply these words belong to. |
text |
string |
yes | This delta alone: a word (a token, in a written call), not the words so far. |
final |
boolean |
yes | False on every delta of a spoken or written reply, which turn.agent closes. True on an entry that carries a whole reply at once, as the platform's own message to a caller nobody could take does. |
start |
number |
no | When these words start, in seconds from the start of the reply's audio, as the voice aligned them. Absent when the voice returned no word timings. |
end |
number |
no | When these words end, in seconds from the start of the reply's audio, as the voice aligned them. Absent when the voice returned no word timings. |
call.attached#
A live call is now served by this socket, mid-conversation: the socket that held it drained, or died, or the gateway restarted. The socket rebuilds the call from this entry and sends its whole prompt and its tools again, because nothing it sent before reached this socket; every tool.call still waiting for an answer follows it, sent again with its own seq.
| field | type | required | meaning |
|---|---|---|---|
app |
string |
yes | The socket that serves the call from now on, as agent.registered named it. |
started |
CallStarted |
yes | How the call opened: who, where, when, as call.started said it. |
state |
object |
yes | The agent's state as the last state.changed of the call left it; empty when none was written. |
seq |
integer |
yes | The last entry of this call before it changed hands. |
claimed |
`string | null` | no |
call.claimed#
This call is the one a page was waiting for: the caller keyed the code the page shows, or the agent claimed it from what they said. From here the page follows the call, and the agent knows the person is on the site.
| field | type | required | meaning |
|---|---|---|---|
code |
string |
yes | The code that was claimed. |
via |
`"keypad" | "agent"` | yes |
call.dialing#
The platform is placing an outbound call and the far end has not answered yet. The first entry of an outbound call's log.
| field | type | required | meaning |
|---|---|---|---|
channel |
Channel |
yes | The endpoint the public came through: a phone call over SIP, the browser widget over WebRTC, or WhatsApp text. |
from |
string |
yes | The number the call shows as coming from, E.164. |
to |
string |
yes | The number being dialed, E.164. |
run |
`string | null` | no |
caller |
`Contact | null` | yes |
external_id |
string |
no | The carrier or SIP call id, once assigned. |
asked_by |
string |
no | Who asked for this call: the member id whose key placed it, or the key id itself when a machine key did. A call back is somebody's, and this is whose. Absent from a gateway that predates it. |
call.ended#
The call is over. Nothing about the conversation follows; call.summary still does.
| field | type | required | meaning |
|---|---|---|---|
reason |
EndReason |
yes | Why the call is over. When the agent's side ends a call in a room for any reason but transferred, the worker deletes the room, which is what hangs up a phone caller; a transfer leaves them with the far end. |
ended_by |
EndedBy |
yes | Whose action ended the call. |
ended_at |
number |
yes | When it ended, unix seconds. |
duration_s |
number |
yes | Seconds from call.started to now. 0 for a call that never started. |
call.line#
The line's hold and mute flags after one of them changed. Both are stated so a reader never has to remember the other.
| field | type | required | meaning |
|---|---|---|---|
held |
boolean |
yes | True while the caller is on hold and hears nothing from the agent. |
muted |
boolean |
yes | True while the agent produces no audio. |
call.ringing#
An inbound call is offered to this agent and has not been answered yet. The first entry of an inbound call's log.
| field | type | required | meaning |
|---|---|---|---|
channel |
Channel |
yes | The endpoint the public came through: a phone call over SIP, the browser widget over WebRTC, or WhatsApp text. |
from |
string |
yes | The calling number in E.164 form, or the web visitor id. |
to |
string |
yes | The number or route that was called. |
route |
Route |
yes | One endpoint to an agent: a channel and, for phone and WhatsApp, the number that answers. |
run |
`string | null` | no |
caller |
`Contact | null` | yes |
external_id |
string |
no | The carrier or SIP call id, for tracing outside Pinecall. |
call.score#
The last entry of a call: what the judges said about it at hang-up, one row per judge. Written after call.summary, and the entry the log seals on.
| field | type | required | meaning |
|---|---|---|---|
passed |
boolean |
no | Whether no judge answered broken. Only a judge that answers verdict sets it: N/A, a classification (choice, score), a deferred or a skipped judge is never a fault of the call. ABSENT when no verdict judge held or broke, which is not the same as false: read not_judged for why nobody answered. |
not_judged |
string |
no | Why nothing judged this call, when nothing did: the judges are not installed on this deployment, or the judging itself failed. Absent on every call a judge answered. |
judges |
Judgment[] |
yes | One row per judge that was run over this call, in the order they were declared. |
panel |
string[] |
no | Every judge this call declared, whether or not it answered: a judge that raised is here and absent from judges. Empty when nothing was ever declared, and absent on entries written before this field existed. |
judge_calls |
integer |
yes | How many requests judging this call put to the judge model, a trigger's yes-or-no among them. |
evals |
integer |
no | How many judges answered this call's question: held, broken or classified. N/A, deferred and skipped are not counted. The org is billed one eval for each, unless own_key. |
own_key |
boolean |
no | The judge model ran on a key of the org's own (the agent's judge, or the org's at PUT /v1/org/judging): the model's bill is the org's, and its evals are never billed. |
judged_by |
`JudgedBy | null` | no |
judge_cost_usd |
number |
no | What those questions cost in US dollars, priced from the judge model's own usage rows. Absent when no usage was reported, never zero. A score written before money in dollars said judge_cost_eur: the same number, read as dollars. |
call.started#
Media is up: the caller and the agent can hear each other, or the text session is open. Everything the agent says and hears comes after this.
| field | type | required | meaning |
|---|---|---|---|
channel |
Channel |
yes | The endpoint the public came through: a phone call over SIP, the browser widget over WebRTC, or WhatsApp text. |
direction |
Direction |
yes | Inbound: the public reached the agent. |
from |
string |
yes | The calling side, E.164 or a visitor id. |
to |
string |
yes | The called side. |
run |
`string | null` | no |
persona |
`string | null` | no |
expects |
`string[] | null` | no |
accepts_when |
`string | null` | no |
declines_when |
`string | null` | no |
caller |
`Contact | null` | yes |
started_at |
number |
yes | When media came up, unix seconds. |
env |
Env |
no | The environment this call ran in: the one the key that holds its agent opens. Absent on entries written before keys knew where they were, which read as production. |
worker |
`string | null` | no |
medium |
`Medium | null` | no |
state |
object |
no | The state the call opens in, when whoever opened it asked for one: a golden, a persona, or ?state= on the chat endpoint. The app applies it after its call hook and before the first render. Absent otherwise: the call opens in the class's own state. |
today |
`string | null` | no |
call.summary#
What the call was about, how it went, what it consumed and what that cost. Written after call.ended, once memory and pricing are done; call.score follows it and seals the log.
| field | type | required | meaning |
|---|---|---|---|
reason |
EndReason |
yes | Why the call is over. |
outcome |
string |
yes | One line on how it went, in the agent's words: booked, no slot, wrong number. |
duration_s |
number |
yes | Seconds of conversation. |
turns |
integer |
yes | How many turns, both sides together. |
usage |
ModelUsage[] |
yes | One row per model used, as the session summed them. |
cost |
Cost |
yes | What the call cost in provider fees and, when the platform priced its own compute (pinecall-compute), the platform's, in US dollars; informational. |
recording |
string |
no | Where the recorder wrote the recording, when one was made; GET /v1/calls/{call}/recording serves it from wherever it is kept, reading only the file's name from this path: on the disk it is always <PINECALL_RECORDINGS>/<call>/audio.ogg or audio.sealed, never another file. |
simulated |
boolean |
no | A simulated caller, not a person, was on the other end: a persona's call, or a golden the simulated caller played out loud. It counts as one simulation and never as minutes. |
run |
`string | null` | no |
call.transferred#
A transfer asked for by the agent or a supervisor finished, one way or the other.
| field | type | required | meaning |
|---|---|---|---|
to |
string |
yes | Where the caller was sent. |
mode |
`TransferMode | null` | no |
ok |
boolean |
yes | True when the far end took the call. |
error |
string |
no | Why it failed, when it did. |
dtmf.received#
A touch tone the caller keyed. Written by the worker, one per tone, from the caller's own leg only: what a person keys is a fact of the call, whether an app reads it as a menu choice or the runtime reads four of them as the code a page is waiting for (call.claimed).
| field | type | required | meaning |
|---|---|---|---|
digit |
`"0" | "1" | "2" |
code |
integer |
yes | RFC 4733's number for it: the digits are themselves, * is 10, # is 11. |
metrics.avatar#
One avatar timing report: livekit's AvatarMetrics, every field. No Pinecall channel has an avatar today.
Data: AvatarMetrics, in Metrics.
metrics.eot#
One end-of-turn prediction: livekit's EOTInferenceMetrics, every field. Has no speech_id; it belongs to the call.
Data: EOTInferenceMetrics, in Metrics.
metrics.eou#
How long closing the caller's turn took: livekit's EOUMetrics, every field. Joined to the turn by speech_id.
Data: EOUMetrics, in Metrics.
metrics.interruption#
The interruption detector's latest inference and running counts: livekit's InterruptionMetrics, every field.
Data: InterruptionMetrics, in Metrics.
metrics.llm#
One LLM request as the session measured it: livekit's LLMMetrics, every field. Joined to the turn by speech_id.
Data: LLMMetrics, in Metrics.
metrics.realtime#
One speech-to-speech response: livekit's RealtimeModelMetrics, every field, its token details nested as the library nests them.
Data: RealtimeModelMetrics, in Metrics.
metrics.stt#
One STT request or stream segment as the session measured it: livekit's STTMetrics, every field. Has no speech_id; it belongs to the call.
Data: STTMetrics, in Metrics.
metrics.tts#
One TTS request as the session measured it: livekit's TTSMetrics, every field. Joined to the turn by speech_id; several per reply, one per segment.
Data: TTSMetrics, in Metrics.
metrics.vad#
The VAD's health for the last second or so: livekit's VADMetrics, every field. About one a second, so ephemeral by default: the console sees it live, the store need not keep it.
Data: VADMetrics, in Metrics.
turn.agent#
The agent's reply is over and this is what was said. With it, everything the session measured about the reply.
| field | type | required | meaning |
|---|---|---|---|
speech_id |
string |
yes | The reply's id, shared with the caller's turn and with the metrics blocks. |
item_id |
string |
no | The session's id for this message in the chat history. |
text |
string |
yes | What was said. When interrupted, only what was actually played. |
interrupted |
boolean |
yes | True when the caller cut the reply short. |
metrics |
AgentTurnMetrics |
yes | What the session measured about the agent's reply. |
turn.user#
The caller's turn is over and this is what they said. With it, everything the session measured about the turn.
| field | type | required | meaning |
|---|---|---|---|
speech_id |
string |
yes | The id of the reply this turn triggers; turn.agent and its llm, tts and eou blocks carry the same id. That is the join. |
item_id |
string |
no | The session's id for this message in the chat history. |
text |
string |
yes | The final transcript, or the message as typed. |
language |
string |
no | The language heard, as a BCP 47 tag. |
transcript_confidence |
number |
no | The recognizer's confidence, 0 to 1, when it reports one. |
metrics |
UserTurnMetrics |
yes | What the session measured about the caller's turn. |
user.state#
The caller's state changed, in the session's own words.
| field | type | required | meaning |
|---|---|---|---|
state |
UserState |
yes | What the platform believes the person on the line is doing right now. |
user.transcript#
Words from the caller as the recognizer hears them. Interim while final is false; the final one becomes turn.user. Interim entries are ephemeral.
| field | type | required | meaning |
|---|---|---|---|
text |
string |
yes | The transcript so far, or the final one. |
final |
boolean |
yes | True when the recognizer will not revise this. |
language |
string |
no | The language heard, as a BCP 47 tag. |
confidence |
number |
no | The recognizer's confidence, 0 to 1, when it reports one. |
vendor.switched#
A stage's vendor failed or came back; serving is the vendor the stage runs on now. Written only for a stage the providers row gives fallbacks, when livekit's fallback adapter marks one of its vendors down or back up. The vendors are named as livekit's plugins name them, as in the metrics blocks, whose metadata also says which vendor served each request.
| field | type | required | meaning |
|---|---|---|---|
stage |
`"llm" | "stt" | "tts"` |
vendor |
string |
yes | The vendor that went down or came back. |
model |
string |
yes | Its model. |
available |
boolean |
yes | False when it went down, true when it answers again. |
serving |
string |
yes | The vendor the stage's next request goes to: the first one still up, in the row's order. |
serving_model |
string |
yes | That vendor's model. |