> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coval.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Standard Chat

> Connect a chat agent over a standard HTTP endpoint and simulate text conversations against it.

Connect a text chat agent to Coval to simulate conversations — useful for chat agents, or for testing a voice agent's logic without placing calls.

Coval supports three chat connection types. Most agents use the **standard** connection, which this page covers. If your agent uses a different transport, use one of the others:

<CardGroup cols={2}>
  <Card title="WebSocket" icon="plug" href="/concepts/agents/connections/chat-websocket">
    A persistent WebSocket connection.
  </Card>

  <Card title="A2A (JSON-RPC)" icon="code" href="/concepts/agents/connections/chat-a2a">
    The A2A v2 JSON-RPC protocol.
  </Card>
</CardGroup>

The standard connection is an HTTP endpoint you provide that returns responses in OpenAI chat-completions format. You can also point Coval directly at an [OpenAI-compatible API](#connecting-an-openai-compatible-api). **Requirement:** a text endpoint Coval can reach.

<Info>
  **How to use this page.** Most agents need only [Quick Start](#quick-start) and the two required fields to connect. Everything else is optional — add initialization or custom headers if your API needs them, use [Advanced Configuration](#advanced-configuration) for non-standard request/response shapes, and treat [Common Configuration Patterns](#common-configuration-patterns) and [Troubleshooting](#troubleshooting) as reference.
</Info>

## Quick Start

**Minimum Required Configuration:**

1. **Chat Endpoint** - The URL where your agent receives messages
2. **Authorization Header** - Authentication credentials for your API

That's it! All other fields are optional and depend on your specific API requirements.

***

## Configuration reference

The standard connection has two required fields; everything else is optional. Jump to any field for details.

| Field                                                  | Required | Description                                                  |
| ------------------------------------------------------ | -------- | ------------------------------------------------------------ |
| [Chat Endpoint](#chat-endpoint-required)               | Yes      | URL where Coval sends conversation messages                  |
| [Authorization Header](#authorization-header-required) | Yes      | Auth credentials sent with every request                     |
| [Initialization Endpoint](#initialization-endpoint)    | No       | Called once before the conversation to set up session state  |
| [Initialization Payload](#initialization-payload)      | No       | JSON body sent to the initialization endpoint                |
| [Custom Data](#custom-data)                            | No       | Extra JSON included in every chat request                    |
| [Custom Headers](#custom-headers)                      | No       | Extra HTTP headers, with dynamic template values             |
| [Response Format](#response-format)                    | No       | Chat Completions (default) or Responses API for tool results |
| [Tool Calls](#tool-calls)                              | No       | Include tool/function calls in the Responses format          |
| [Payload Wrapper](#payload-wrapper)                    | No       | Nest the payload under a field name your API requires        |
| [Input Template](#input-template)                      | No       | Reshape the request body for non-standard APIs               |
| [Response Message Path](#response-message-path)        | No       | Where to read the agent's reply in a non-standard response   |
| [Strip Message Timestamps](#strip-message-timestamps)  | No       | Remove timestamps if your API rejects them                   |

## Core Configuration

### Chat Endpoint (Required)

The primary URL where Coval sends conversation messages during simulation.

**Format:** Full HTTPS URL

```
https://api.yourdomain.com/chat
```

**Requirements:**

* Must use HTTPS (HTTP will be auto-upgraded)
* Cannot use local/private IP addresses
* Must return JSON responses

**Example:**

```
https://api.yourdomain.com/v1/chat
```

***

### Authorization Header (Required)

Authentication credentials sent with every API request.

**Common Formats:**

#### Bearer Token

```
Bearer your-secret-token-here
```

#### API Key

```
API-Key your-api-key-here
```

#### Custom Authorization

```
Custom-Auth your-custom-format
```

**UI Tip:** Use the dropdown to select your auth type, then paste your token/key. The system automatically formats the header correctly.

***

## Connecting an OpenAI-compatible API

Instead of pointing at your own agent backend, you can point Coval directly at any OpenAI-compatible chat completions API. This is useful for testing a model and system prompt without a backend in front of it — Coval calls the API and drives the model itself.

| Field                    | Required | Description                                                                                           |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------------- |
| **Chat Endpoint**        | Yes      | URL of the OpenAI-compatible chat completions API (e.g. `https://api.openai.com/v1/chat/completions`) |
| **Authentication Token** | Yes      | API key for the endpoint (stored encrypted)                                                           |
| **Model**                | No       | Which model to use (default `gpt-4o`)                                                                 |
| **System Prompt**        | No       | System instructions defining the agent's behavior                                                     |
| **Temperature**          | No       | Response randomness, 0.0–2.0 (default 1.0)                                                            |
| **Max Tokens**           | No       | Maximum tokens to generate (≤ 100,000)                                                                |

Everything else on this page — initialization, custom headers, input templates, response format — applies the same way.

***

## Standard Protocol

The standard integration for chat uses an HTTP, JSON-based API endpoint that you provide. When running a simulation, Coval's simulator, acting as a user, will connect to the endpoint to get responses from your agent. We use OpenAI's chat completions format, although we also support receiving responses in the OpenAI responses format. Query strings are not allowed in URLs.

***

## Chat Messages

Your chat endpoint should be an HTTPS URL that will respond to POST requests with a JSON body. If an Authorization token was provided, it will be included in the headers.

**Initial Request from Coval:**

```json theme={null}
{
  "sessionId": "XXXX",
  "customData": {},
  "messages": [
    {
      "role": "user",
      "content": "Initial reach out text"
    }
  ]
}
```

**Expected Response Format:**

Standard format:

```json theme={null}
{
  "messages": [
    {
      "role": "assistant",
      "content": "Response to the initial text"
    }
  ]
}
```

Or in the newer Responses format:

```json theme={null}
{
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "text_output",
          "text": "Thanks for contacting us"
        }
      ]
    }
  ]
}
```

***

## Optional Configuration

### Initialization Endpoint

Called once before the conversation starts to set up session state.

**When to use:**

* Your API requires session initialization
* You need to obtain a session ID or auth token
* You want to pre-configure conversation context

**Format:** Full HTTPS URL

```
https://api.yourdomain.com/init
```

**Example Response:**

```json theme={null}
{
  "sessionId": "abc-123-def",
  "userId": "user-456",
  "conversationId": "conv-789"
}
```

**How it works:**

1. Coval calls the initialization endpoint with your [initialization payload](#initialization-payload)
2. The response is captured and made available to subsequent chat requests via template variables like `{{sessionId}}` or `{{init_response.conversationId}}` in your [input template](#input-template) and [custom headers](#custom-headers)

<Note>
  The initialization endpoint is called **before** any chat messages are sent. The [input template](#input-template) is only used for chat requests — it does not affect the initialization call.
</Note>

***

### Initialization Payload

JSON payload sent to the initialization endpoint.

**Format:** Valid JSON

```json theme={null}
{
  "user_id": "test-user",
  "config": {
    "language": "en",
    "temperature": 0.7
  }
}
```

**Template Variables Available:**

| Variable                   | Description                   | Example Value         |
| -------------------------- | ----------------------------- | --------------------- |
| `{{simulation_output_id}}` | Unique ID for this simulation | `"sim-abc-123"`       |
| `{{persona.field}}`        | Data from persona metadata    | `{{persona.user_id}}` |

**Example with Variables:**

```json theme={null}
{
  "session_id": "{{simulation_output_id}}",
  "user_context": {
    "user_id": "{{persona.user_id}}",
    "preferences": {}
  }
}
```

**Persona Integration:**

To use `{{persona.field}}` variables, add `initialization_parameters` to your Persona's metadata:

```json theme={null}
{
  "initialization_parameters": {
    "user_id": "customer-123",
    "account_type": "premium"
  }
}
```

Then reference in payload:

```json theme={null}
{
  "user_id": "{{persona.user_id}}",
  "account_type": "{{persona.account_type}}"
}
```

***

### Custom Data

Additional JSON data included in every chat request (for APIs using standard payload format).

**Format:** Valid JSON

```json theme={null}
{
  "metadata": {
    "source": "coval-evaluation",
    "version": "1.0"
  },
  "context": {
    "department": "sales"
  }
}
```

**How it's sent:**

```json theme={null}
{
  "messages": [...],
  "customData": {
    "metadata": {...},
    "context": {...}
  }
}
```

**Note:** Only used when NOT using `input_template`. If you use `input_template`, reference custom data with `{{custom_data.field}}` instead.

***

### Custom Headers

Additional HTTP headers sent with every chat request, with support for dynamic values from the initialization response, the test case, and the agent.

**Format:** Valid JSON object with string keys and values

```json theme={null}
{
  "X-Session-ID": "{{sessionId}}",
  "X-User-ID": "{{init_response.user.id}}",
  "X-Custom-Header": "static-value"
}
```

**Template Variables Available:**

| Variable                    | Description                                                                                                        | Example                                  |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- |
| `{{sessionId}}`             | Session ID from init response                                                                                      | Extracted from `init_response.sessionId` |
| `{{simulation_output_id}}`  | Unique simulation ID                                                                                               | Generated by Coval                       |
| `{{init_response.path}}`    | Any nested field from init response                                                                                | `{{init_response.auth.token}}`           |
| `{{test_case.<attribute>}}` | A [test case attribute](/concepts/attributes/overview) — per-test-case state, injected into the header at run time | `{{test_case.account_id}}`               |
| `{{agent.<attribute>}}`     | An [agent attribute](/concepts/attributes/overview) — shared across all test cases for this agent                  | `{{agent.region}}`                       |

Because `custom_headers` sends string header values, `{{test_case.<attribute>}}` and `{{agent.<attribute>}}` should resolve to the exact header text you want to send. If you want a numeric- or boolean-looking header value, store that exact text in the attribute. Objects and arrays are not supported in headers; use [`input_template`](#input-template) instead for structured values.

**Common Use Cases:**

**Use Case 1: Session ID in Header**

```json theme={null}
{
  "X-Session-ID": "{{sessionId}}"
}
```

**Use Case 2: Nested Auth Token**

```json theme={null}
{
  "X-Auth-Token": "{{init_response.auth.token}}"
}
```

**Use Case 3: Mixed Static and Dynamic**

```json theme={null}
{
  "X-Session-ID": "{{sessionId}}",
  "X-API-Version": "v2",
  "X-Simulation-ID": "{{simulation_output_id}}"
}
```

**Use Case 4: Per-Test-Case State**

Pass state (e.g. an account or customer ID) into your agent so its backend resolves the right context for each test case. Add the value as a [test case attribute](/concepts/attributes/overview) (one value per case), then reference it in the header. Coval substitutes each test case's value at run time:

```json theme={null}
{
  "X-Account-Id": "{{test_case.account_id}}"
}
```

**Field Size Limit:** 16kB maximum

***

## Advanced Configuration

### Response Format

Determines the format for tool call responses sent to your API.

**Options:**

#### Chat Completions (Default)

Standard OpenAI format for tool responses:

```json theme={null}
{
  "role": "tool",
  "content": "result",
  "tool_call_id": "call_123"
}
```

#### Responses API

Alternative format for function call outputs:

```json theme={null}
{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "result"
}
```

Configure the response format by adding `response_format` to your model configuration:

```json theme={null}
{
  "response_format": "responses_api",
  "chat_endpoint": "https://api.your-company.com/chat",
  "authorization_header": "Bearer your-api-key"
}
```

Coval will respond with the entire chat history in the format specified:

<Tabs>
  <Tab title="Chat Completions (Default)">
    ```json theme={null}
    {
      "sessionId": "XXXX",
      "customData": {},
      "messages": [
        {
          "role": "user",
          "content": "Initial reach out text"
        },
        {
          "role": "assistant",
          "content": "Thanks for contacting us"
        },
        {
          "role": "user",
          "content": "When will my order arrive?"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Responses API">
    ```json theme={null}
    {
      "sessionId": "XXXX",
      "customData": {},
      "messages": [
        {
          "role": "user",
          "content": "Initial reach out text"
        },
        {
          "role": "assistant",
          "content": [
            {
              "type": "text_output",
              "text": "Thanks for contacting us"
            }
          ]
        },
        {
          "role": "user",
          "content": "When will my order arrive?"
        }
      ]
    }
    ```
  </Tab>
</Tabs>

**When to use:** Only change this if your API explicitly requires the Responses API format for tool calls. Most APIs use the default Chat Completions format.

***

### Tool Calls

You can include tool/function calls in the Responses format:

```json theme={null}
{
  "messages": [
    {
      "type": "function_call",
      "name": "get_order_date",
      "arguments": "{\"shipment_id\": \"xx555\"}"
    },
    {
      "role": "assistant",
      "content": [
        {
          "type": "text_output",
          "text": "Your order should arrive next Tuesday"
        }
      ]
    }
  ]
}
```

***

### Payload Wrapper

Wraps the entire payload in a specified field name.

**When to use:** Your API requires all payloads nested under a specific key (e.g., `data`, `request`, `body`).

**Example:**

**Without wrapper:**

```json theme={null}
{
  "messages": [...],
  "customData": {...}
}
```

**With wrapper set to `"data"`:**

```json theme={null}
{
  "data": {
    "messages": [...],
    "customData": {...}
  }
}
```

**Common Values:**

* `data`
* `request`
* `body`
* `payload`

***

### Input Template

Completely customize the JSON payload sent to your **chat endpoint** on each conversation turn.

<Note>
  The input template is **not** used for the [initialization endpoint](#initialization-endpoint) — that call always uses the [initialization payload](#initialization-payload). The simulation flow is:

  1. Coval calls your initialization endpoint with the initialization payload
  2. The init response is captured
  3. For each chat turn, Coval uses the input template to build the request to your chat endpoint — and you can reference fields from the init response (e.g. `{{init_response.conversation_id}}`)
</Note>

**When to use:**

* Your API expects a non-standard payload format
* You need to include specific fields from initialization response
* You want fine-grained control over the request structure

**Format:** JSON with template variable placeholders

**Available Template Variables:**

| Variable                    | Type   | Description                                                                                                                                          |
| --------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{{messages}}`              | Array  | Full conversation history                                                                                                                            |
| `{{latest_message}}`        | String | Most recent user message content                                                                                                                     |
| `{{sessionId}}`             | String | Session ID (from init response or simulation ID)                                                                                                     |
| `{{simulation_output_id}}`  | String | Unique simulation identifier                                                                                                                         |
| `{{custom_data}}`           | Object | The custom data object                                                                                                                               |
| `{{custom_data.field}}`     | Any    | Specific field from custom data                                                                                                                      |
| `{{any.nested.path}}`       | Any    | Extract any field from init response using dot notation                                                                                              |
| `{{test_case.<attribute>}}` | Any    | A [test case attribute](/concepts/attributes/overview) — per-test-case state; quote it only when the resolved value is a string                      |
| `{{agent.<attribute>}}`     | Any    | An [agent attribute](/concepts/attributes/overview) — shared across all test cases for this agent; quote it only when the resolved value is a string |

**Example Templates:**

**Example 1: Simple Custom Format**

```json theme={null}
{
  "user_input": "{{latest_message}}",
  "session_id": "{{sessionId}}",
  "context": {{custom_data}}
}
```

**Example 2: Nested Init Response Fields**

```json theme={null}
{
  "messages": {{messages}},
  "user_id": "{{init_response.user.id}}",
  "conversation_id": "{{init_response.conversation.id}}",
  "api_key": "{{init_response.auth.api_key}}"
}
```

**Example 3: String Substitution**

```json theme={null}
{
  "input": "User said: {{latest_message}}",
  "session": "{{sessionId}}",
  "metadata": {
    "source": "coval",
    "user": "{{custom_data.user_id}}"
  }
}
```

**Note:** When using `input_template`, the `custom_data` field is ignored. Reference custom data using `{{custom_data}}` or `{{custom_data.field}}` in your template instead.

<Warning>
  **Quoting rules for template variables:**

  * **Object/Array variables** (`{{messages}}`, `{{custom_data}}`) substitute to valid JSON literals — do **not** wrap them in quotes.
  * **String variables** (`{{sessionId}}`, `{{latest_message}}`, `{{init_response.*}}`) substitute to plain text — you **must** wrap them in quotes.
  * **`Any` variables** (`{{custom_data.field}}`, `{{any.nested.path}}`, `{{test_case.<attribute>}}`, `{{agent.<attribute>}}`) follow the resolved value's JSON type — quote strings, but leave numbers, booleans, objects, and arrays unquoted.

  For example, `"conversation_id": {{init_response.conversation.id}}` produces invalid JSON because the substituted value is not quoted. Use `"conversation_id": "{{init_response.conversation.id}}"` instead.
</Warning>

***

### Response Message Path

Tells Coval where to find the assistant's message in your API response using dot notation.

**When to use:** Your API returns a non-standard response format.

**Default Behavior (when not set):**
Expects response in this format:

```json theme={null}
{
  "messages": [
    {
      "role": "assistant",
      "content": "The response text"
    }
  ]
}
```

**Custom Path Examples:**

**Example 1: Direct Field**

```
Response Message Path: output_message
```

Extracts from:

```json theme={null}
{
  "output_message": "The assistant response",
  "metadata": {...}
}
```

**Example 2: Nested Object**

```
Response Message Path: data.response.text
```

Extracts from:

```json theme={null}
{
  "data": {
    "response": {
      "text": "The assistant response"
    }
  }
}
```

**Example 3: Array Index**

```
Response Message Path: choices.0.message.content
```

Extracts from:

```json theme={null}
{
  "choices": [
    {
      "message": {
        "content": "The assistant response"
      }
    }
  ]
}
```

**Path Notation Rules:**

* Use `.` to navigate nested objects: `data.response.text`
* Use numeric indices for arrays: `choices.0.message`
* Combine for complex paths: `data.results.0.output.text`

***

### Strip Message Timestamps

Removes `timestamp` fields from messages before sending to your API.

**When to use:** Your API rejects requests containing timestamp fields.

**Default:** Disabled (timestamps included)

**Example:**

**With timestamps (default):**

```json theme={null}
{
  "messages": [
    {
      "role": "user",
      "content": "Hello",
      "timestamp": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**With stripping enabled:**

```json theme={null}
{
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ]
}
```

**Common Error Pattern:**

```json theme={null}
{
  "message": ["messages.0.property timestamp should not exist"],
  "statusCode": 400
}
```

If you see this error, enable "Strip Message Timestamps".

***

## Ending the Chat

You can end the conversation by setting "status" to "ended" in your response:

```json theme={null}
{
  "status": "ended",
  "messages": [...]
}
```

***

## Common Configuration Patterns

### Pattern 1: OpenAI-Compatible API

```
Chat Endpoint: https://api.yourdomain.com/chat
Authorization: Bearer sk-your-key-here

(All other fields: leave empty/default)
```

### Pattern 2: API with Session Initialization

```
Chat Endpoint: https://api.yourdomain.com/chat
Initialization Endpoint: https://api.yourdomain.com/init
Authorization: API-Key your-key-here
Initialization Payload:
{
  "user_id": "{{persona.user_id}}",
  "session_id": "{{simulation_output_id}}"
}
```

### Pattern 3: Custom API Format with Template

```
Chat Endpoint: https://api.yourdomain.com/v1/message
Authorization: Bearer your-token
Input Template:
{
  "user_input": "{{latest_message}}",
  "session_id": "{{sessionId}}",
  "conversation_history": {{messages}}
}
Response Message Path: data.response.text
```

### Pattern 4: API with Payload Wrapper

```
Chat Endpoint: https://api.yourdomain.com/chat
Authorization: Bearer your-token
Payload Wrapper: data
```

### Pattern 5: Complex Custom Format

```
Chat Endpoint: https://api.yourdomain.com/chat
Initialization Endpoint: https://api.yourdomain.com/sessions/create
Authorization: Bearer static-token
Custom Headers:
{
  "X-Session-ID": "{{sessionId}}",
  "X-User-Context": "{{init_response.user.id}}"
}
Input Template:
{
  "messages": {{messages}},
  "user_id": "{{init_response.user.id}}",
  "api_version": "v2"
}
Response Message Path: response.text
Strip Message Timestamps: true
```

***

## Troubleshooting

| Symptom                                               | Likely cause                                                                                | Fix                                                                                                          |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Failed to run simulation due to an unexpected error` | Invalid input template — unquoted string variables, malformed JSON, or bad field references | Check the template for valid JSON and wrap string variables in quotes (see [quoting rules](#input-template)) |
| `Invalid JSON response from endpoint`                 | API returned a non-JSON response                                                            | Return `Content-Type: application/json`                                                                      |
| `Could not extract message from path 'X' in response` | [Response message path](#response-message-path) doesn't match your response structure       | Verify the dot-notation path matches your actual response                                                    |
| `messages.0.property timestamp should not exist`      | API rejects timestamp fields                                                                | Enable [Strip Message Timestamps](#strip-message-timestamps)                                                 |
| Tool calls not showing in transcript                  | Tool-call extraction not configured                                                         | Ensure the API returns OpenAI-compatible format, or contact support for custom extraction                    |
| Session ID not working in headers                     | Template variable isn't being substituted                                                   | Confirm the init endpoint returns a `sessionId` field and check [custom headers](#custom-headers)            |
