# Replace a test (/docs/api/voice-agents/agent-tests/replaceAgentTest)

> For the complete documentation index, see [llms.txt](https://docs.tandemcx.ai/llms.txt). Any page on this site can be fetched as Markdown by appending `.md` to its URL.

`PUT /v1/agent-media/agents/{agentId}/tests/{testId}`

Replace a test

Replace the test's definition. `testId`, `agentId`, `source`, `createdBy`, and `createdAt` keep their stored values; a `source` in the body is ignored. Runs already started keep the definition they started with.

## Request

**Base URL:** `https://api.tandemcx.ai`

**Authentication:** `Authorization: Bearer <token>` — A Tandem bearer token. Requests are scoped to the organization the token belongs to; no organization header is required.

### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `agentId` | string | Yes | The agent's unique ID. |
| `testId` | string | Yes | The test's unique ID. |

### Body (`application/json`) — required

A test definition. `scripted` tests need `scripted` and no `simulated`; `simulated` tests need `simulated` and no `scripted`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | — |
| `description` | string | No | — |
| `mode` | enum: `scripted` \| `simulated` | Yes | `scripted` sends fixed caller messages in order. `simulated` has a model play the caller from a persona and a goal. |
| `scripted` | object | No | — |
| `scripted.turns` | array of one of: string \| object | Yes | The caller's messages and keypad entries, in order. A keypad entry right after a message is what the caller types when the agent's reply calls `capture_dtmf`; one no capture takes is sent as its own turn, the way a call delivers keypresses with Keypad Input on. |
| `simulated` | object | No | — |
| `simulated.persona` | string | Yes | — |
| `simulated.goal` | string | Yes | — |
| `simulated.maxTurns` | integer | No | Agent turns before the case ends with `max_turns`. |
| `stubs` | object | No | Keyed by tool name. A tool is stubbed by default: with no entry it returns `{}`, and with an entry it returns `response`. `live: true` calls the real tool instead. |
| `checks` | array of one of: object \| object \| object \| object \| object \| object \| object | No | — |
| `criteria` | array of string | No | Statements an LLM judge grades against the transcript. |
| `repeat` | integer | No | Attempts per run. Absent means the settings' `defaultRepeat`. |
| `caller` | object | No | Who is calling. `ani` fills `{{sys.telephony.ani}}` and is the number the contact and memory belong to. `contact` is a contact found for that number with these attributes; it exists only in the test, and no real contact is ever read or written. `contact` requires `ani`. |
| `caller.ani` | string | No | E.164, such as +15551234567. |
| `caller.contact` | object | No | — |
| `caller.contact.attributes` | object | Yes | — |
| `workflowVariables` | object | No | What the calling workflow passed in, read as `{{vars.name}}`. |
| `memory` | object | No | A returning caller's memory record, read at the version's memory depth. Requires `caller.ani`. Without it, memory is off during the test. |
| `memory.callCount` | integer | Yes | — |
| `memory.synopsis` | string | No | — |
| `memory.rollingSummary` | string | No | — |
| `memory.openThreads` | string | No | — |

## Responses

### 200 — The updated test.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `test` | object | No | A test definition. `scripted` tests need `scripted` and no `simulated`; `simulated` tests need `simulated` and no `scripted`. |
| `test.name` | string | Yes | — |
| `test.description` | string | No | — |
| `test.mode` | enum: `scripted` \| `simulated` | Yes | `scripted` sends fixed caller messages in order. `simulated` has a model play the caller from a persona and a goal. |
| `test.scripted` | object | No | — |
| `test.scripted.turns` | array of one of: string \| object | Yes | The caller's messages and keypad entries, in order. A keypad entry right after a message is what the caller types when the agent's reply calls `capture_dtmf`; one no capture takes is sent as its own turn, the way a call delivers keypresses with Keypad Input on. |
| `test.simulated` | object | No | — |
| `test.simulated.persona` | string | Yes | — |
| `test.simulated.goal` | string | Yes | — |
| `test.simulated.maxTurns` | integer | No | Agent turns before the case ends with `max_turns`. |
| `test.stubs` | object | No | Keyed by tool name. A tool is stubbed by default: with no entry it returns `{}`, and with an entry it returns `response`. `live: true` calls the real tool instead. |
| `test.checks` | array of one of: object \| object \| object \| object \| object \| object \| object | No | — |
| `test.criteria` | array of string | No | Statements an LLM judge grades against the transcript. |
| `test.repeat` | integer | No | Attempts per run. Absent means the settings' `defaultRepeat`. |
| `test.caller` | object | No | Who is calling. `ani` fills `{{sys.telephony.ani}}` and is the number the contact and memory belong to. `contact` is a contact found for that number with these attributes; it exists only in the test, and no real contact is ever read or written. `contact` requires `ani`. |
| `test.caller.ani` | string | No | E.164, such as +15551234567. |
| `test.caller.contact` | object | No | — |
| `test.caller.contact.attributes` | object | Yes | — |
| `test.workflowVariables` | object | No | What the calling workflow passed in, read as `{{vars.name}}`. |
| `test.memory` | object | No | A returning caller's memory record, read at the version's memory depth. Requires `caller.ani`. Without it, memory is off during the test. |
| `test.memory.callCount` | integer | Yes | — |
| `test.memory.synopsis` | string | No | — |
| `test.memory.rollingSummary` | string | No | — |
| `test.memory.openThreads` | string | No | — |
| `test.testId` | string | No | — |
| `test.agentId` | string | No | — |
| `test.source` | enum: `user` \| `sparky` \| `suggestion` | No | — |
| `test.createdBy` | string | No | — |
| `test.createdAt` | string (date-time) | No | — |
| `test.updatedAt` | string (date-time) | No | — |

### 400 — `error` is `invalid_test`, with `errors` naming each offending field; or `invalid_agentId` for a malformed path.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | string | No | — |
| `errors` | array of object | No | — |
| `errors[].field` | string | No | Dotted path to the offending field. |
| `errors[].message` | string | No | — |

### 401 — Missing, malformed, or expired bearer token.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | string | No | A stable machine-readable code. |
| `message` | string | No | A human-readable explanation, safe to show to an end user. Present on errors an operator can act on; browser clients display this field in preference to a generic fallback. |
| `limit` | integer | No | Present on some errors (e.g. `prompt_too_long`) that reject a value against a fixed bound: the bound itself. |
| `length` | integer | No | Present on some errors (e.g. `prompt_too_long`) that reject a value against a fixed bound: the measured size of the rejected value. |
| `reason` | string | No | Present on some errors (e.g. `invalid_avatar_image`) that have more than one cause: which one applied. |

### 404 — `error` is `not_found`: the resource does not exist.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | string | No | A stable machine-readable code. |
| `message` | string | No | A human-readable explanation, safe to show to an end user. Present on errors an operator can act on; browser clients display this field in preference to a generic fallback. |
| `limit` | integer | No | Present on some errors (e.g. `prompt_too_long`) that reject a value against a fixed bound: the bound itself. |
| `length` | integer | No | Present on some errors (e.g. `prompt_too_long`) that reject a value against a fixed bound: the measured size of the rejected value. |
| `reason` | string | No | Present on some errors (e.g. `invalid_avatar_image`) that have more than one cause: which one applied. |
