- 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.
Two things to take away from the table:
- Per-test-case identity works on Chat, Inbound SIP, Outbound, Chat WebSocket, and WebSocket voice. These connection fields substitute
{{test_case.*}}, 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. - Inbound SIP can also receive per-run identity. Put the value in the launch request’s
metadata.customerobject and reference it from a custom SIP header with{{run_metadata.<key>}}. This is useful when an external job or temporary account is created immediately before each run. - 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.