userdata is what the agent knows about the person it is talking to. You seed it on the request that starts the session, the agent reads it as {{userdata.field}}, tools write to it mid-call, and it is merged onto that person’s contact record when the session ends — so their next conversation can start from it. It is one bag per session, shared by every agent on that session. If you want per-session configuration rather than facts about a person, that is vars.

Seeding it

On the call token, the outbound call and the conversation, userdata is a JSON object and values may be any JSON type. On a batch recipient, values are strings. An inbound phone call carries no request of yours, so nothing seeds it there. What an inbound agent starts with is whatever it already learned about that caller — see conversation memory.
Keys beginning with _talqing are reserved for the runtime’s own session bookkeeping and are refused with a 400. They are also stripped from everything the platform stores or hands back, so you will never see one.

Reading it

{{userdata.field}} is substituted in the prompt, the greeting and every tool operation. A field that is not in the bag resolves to nothing — so write the prompt so it still reads when one is absent:
The prompt and greeting resolve once, when the agent is built — before it answers, or the moment a handoff target takes over. A tool resolves per run, so a tool always sees the current bag including whatever an earlier tool on the same call wrote.
A greeting is spoken exactly as it resolves. Runs of leftover whitespace are collapsed, but punctuation is not fixed up. "Hi {{userdata.name}}, thanks for calling" becomes "Hi , thanks for calling" when there is no name. Only put a field in the greeting when the request that starts the session always supplies it. A prompt is safer: you can tell the model to work around a blank.
Reading a userdata key that no operation in the same tool publishes first is a warning at publish, not an error, because the session may well already hold it:

Tools writing it

Two operations write into userdata, both taking store: "userdata":
  • publish_fields on an operation that returns something — pull a value out of the response and keep it.
  • The set_variable operation — write a value you supply.
store: "tooldata" is the other choice and it is not the same thing: tooldata lives for one tool run and is gone afterwards. The full rules are on templating and data. On a web voice or video call, your own page can also read and patch userdata mid-call over the browser SDK — see web call features.

One bag, every agent

Everything on the session shares one userdata: the entry agent, every team member, and any handoff target. A field written by a tool on the first agent is readable by the third. This is true regardless of a handoff’s context policy — summary narrows what the target model is shown of the conversation, and does nothing to userdata.

What happens when the session ends

Two writes, in one transaction:
  • The call’s final userdata is stored on the call itself. GET /v1/calls/{id} returns it as userdata — what this call ended holding.
  • The same bag replaces the contact record: what the platform knows about this person across all their conversations. GET /v1/calls/{id} returns that as contact_userdata.
The contact record is written on every call, whatever the agent’s conversation.context is. Reading it back at the start of a call is a choice (conversation.initialize_userdata); keeping it current is not.
The contact record is replaced, not merged: the bag the call ended with is what the contact record then holds afterwards.That is invisible while the call also started from it — the default conversation.context: "none" does not load it, so on that setting each call overwrites the contact record with only what that call held. If you rely on a person’s record accumulating across calls, set conversation.context to summary or transcript and leave initialize_userdata on, so every call starts from the record it is about to replace.
When a call does read it back, the request’s own userdata is layered on top: what you send wins over what was remembered.

contact_key

contact_key is your stable id for the person, and it is what makes a contact record a person’s record rather than a call’s. The same key always reaches the same identity, so userdata written on one call is there on the next. GET /v1/calls?contact_key=... filters a customer’s whole history. See conversations.

A worked example: one agent, personalized per call

One published agent. Nothing about it changes per call; only the bag does. The prompt:
The greeting:
Placing the call:
During the call, look_up_order publishes last_order_id and last_order_status into userdata. When the call ends, all five fields are on Priya’s contact record. An agent whose conversation.context is summary or transcript, with initialize_userdata left on, starts her next call already knowing them, without you sending anything.

Where to see it

The call detail view shows the final userdata for the call and the contact record beside it — see calls. GET /v1/calls/{session_id} returns both: userdata for this call, contact_userdata for the person.

Next

Conversation memory

What a new call knows about earlier ones, and initialize_userdata.

Variables

vars, system_vars, and every templating rule.

Templating and data

publish_fields, set_variable, and tooldata versus userdata.