- In the conversation — the persona supplies the credentials when the agent asks for them, entering them on the keypad (DTMF) or saying them aloud. Works for every agent type.
- Through the connection — you pass the user’s identity to the agent out of band, in a request header or payload, so your backend resolves the account before the first turn. Agent-type-specific; see Passing the user through the connection.
Whichever approach you use, the user you authenticate as must actually exist
in your system, set up in the state your test needs. You’re impersonating
a real record your agent can look up — not inventing one.
The simplest case: one QA user
If every test runs as the same user, you don’t need attributes or per-test-case setup. Put the credentials in a persona and use that persona for every simulation. This requires a QA user that already exists in your database with the right setup — a real record the agent can authenticate against. Create that account first (a real phone number and PIN the agent will accept), in whatever state your test needs. In the persona’s Persona Characteristics prompt, describe the credentials and exactly when to enter them. For a voice agent, the persona enters digits as DTMF tones with thedtmf tool rather than speaking them — spoken digits are prone to speech-to-text errors, and most IVRs expect keypad input anyway. Give it explicit, structured instructions:
Varying the user per test case
To exercise that second axis — what the agent’s system tells it about you — authenticate as a different user in each test case, so the agent looks up different backend state each time. The first axis you drive through the conversation; this one you drive through which authenticated user you present as. For example, suppose your agent behaves differently depending on whether the caller already has an appointment. You’d want:- one test case that authenticates as a user who has already booked an appointment (the agent checks and sees it), and
- one that authenticates as a user who has not — the same thing said, two different agent behaviors.
Passing the user through the connection
Sometimes you don’t want the user to authenticate in the conversation at all — you want the agent to already know who’s calling when the conversation starts. Most agent types let you pass the user’s identity (auser_id, account ID, or user token) directly through the connection, so your backend resolves the right account before the first turn.
This is different from your agent authenticating Coval (API keys, bearer tokens on your webhook) — that’s covered in each connection type’s own page. The table below is only about passing the user.
| Connection type | How you pass the user | Varies per test case? |
|---|---|---|
| Chat (HTTP) | Put the user’s identifier in custom_headers (e.g. X-Account-Id) or in the request body via the input_template | Yes — both accept {{test_case.*}} and {{agent.*}}, resolved per test case |
| Inbound Voice | On a SIP connection, carry user_id / account in custom_sip_headers. A plain PSTN phone number can’t pass headers — authenticate in-conversation instead. | Yes (SIP) — SIP headers accept {{test_case.*}}, set via mutations |
| Outbound Voice | Put the user’s identity (e.g. account_id, user_token) in the trigger_call_payload; phone_number_key selects which number is dialed | Yes — the trigger payload accepts {{test_case.*}} |
| Chat A2A | Carry a user identifier in custom_headers (A2A message bodies are text-only, so identity rides in headers) | No — headers are static per agent |
| Chat WebSocket | Send the user in initialization_json on connect, or in custom_headers on the handshake | No — resolved per agent, not per test case |
| WebSocket (voice) | Send the user in initialization_json after the upgrade, or in custom_headers on the upgrade | No — resolved per agent, not per test case |
| SMS | The phone number identifies the user | Number is per agent; any other identity in-conversation |
| Pipecat · OpenAI Realtime · Gemini Live | No transport hook to pass the user | Authenticate in-conversation (persona supplies credentials) |
- Per-test-case identity works on Chat, Inbound SIP, and Outbound. Those three substitute
{{test_case.*}}into the header, SIP header, or payload, so one test set can drive many different users out of band — the same way the persona prompt does in the conversation. On the other connection types the connection config is fixed per agent, so to vary the user per test case you authenticate in-conversation. - For voice-to-voice and plain PSTN, in-conversation is the only option. OpenAI Realtime, Gemini Live, and Pipecat connect Coval straight to the model or platform, and a PSTN phone number strips custom headers — there’s no transport to carry the user’s identity, so the persona/DTMF approach from the sections above is how you authenticate.