Skip to main content
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:

WebSocket

A persistent WebSocket connection.

A2A (JSON-RPC)

The A2A v2 JSON-RPC protocol.
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. Requirement: a text endpoint Coval can reach.
How to use this page. Most agents need only 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 for non-standard request/response shapes, and treat Common Configuration Patterns and Troubleshooting as reference.

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.
FieldRequiredDescription
Chat EndpointYesURL where Coval sends conversation messages
Authorization HeaderYesAuth credentials sent with every request
Initialization EndpointNoCalled once before the conversation to set up session state
Initialization PayloadNoJSON body sent to the initialization endpoint
Custom DataNoExtra JSON included in every chat request
Custom HeadersNoExtra HTTP headers, with dynamic template values
Response FormatNoChat Completions (default) or Responses API for tool results
Tool CallsNoInclude tool/function calls in the Responses format
Payload WrapperNoNest the payload under a field name your API requires
Input TemplateNoReshape the request body for non-standard APIs
Response Message PathNoWhere to read the agent’s reply in a non-standard response
Strip Message TimestampsNoRemove 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.
FieldRequiredDescription
Chat EndpointYesURL of the OpenAI-compatible chat completions API (e.g. https://api.openai.com/v1/chat/completions)
Authentication TokenYesAPI key for the endpoint (stored encrypted)
ModelNoWhich model to use (default gpt-4o)
System PromptNoSystem instructions defining the agent’s behavior
TemperatureNoResponse randomness, 0.0–2.0 (default 1.0)
Max TokensNoMaximum 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:
{
  "sessionId": "XXXX",
  "customData": {},
  "messages": [
    {
      "role": "user",
      "content": "Initial reach out text"
    }
  ]
}
Expected Response Format: Standard format:
{
  "messages": [
    {
      "role": "assistant",
      "content": "Response to the initial text"
    }
  ]
}
Or in the newer Responses format:
{
  "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:
{
  "sessionId": "abc-123-def",
  "userId": "user-456",
  "conversationId": "conv-789"
}
How it works:
  1. Coval calls the initialization endpoint with your 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 and custom headers
The initialization endpoint is called before any chat messages are sent. The input template is only used for chat requests — it does not affect the initialization call.

Initialization Payload

JSON payload sent to the initialization endpoint. Format: Valid JSON
{
  "user_id": "test-user",
  "config": {
    "language": "en",
    "temperature": 0.7
  }
}
Template Variables Available:
VariableDescriptionExample Value
{{simulation_output_id}}Unique ID for this simulation"sim-abc-123"
{{persona.field}}Data from persona metadata{{persona.user_id}}
Example with Variables:
{
  "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:
{
  "initialization_parameters": {
    "user_id": "customer-123",
    "account_type": "premium"
  }
}
Then reference in payload:
{
  "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
{
  "metadata": {
    "source": "coval-evaluation",
    "version": "1.0"
  },
  "context": {
    "department": "sales"
  }
}
How it’s sent:
{
  "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
{
  "X-Session-ID": "{{sessionId}}",
  "X-User-ID": "{{init_response.user.id}}",
  "X-Custom-Header": "static-value"
}
Template Variables Available:
VariableDescriptionExample
{{sessionId}}Session ID from init responseExtracted from init_response.sessionId
{{simulation_output_id}}Unique simulation IDGenerated by Coval
{{init_response.path}}Any nested field from init response{{init_response.auth.token}}
{{test_case.<attribute>}}A test case attribute — per-test-case state, injected into the header at run time{{test_case.account_id}}
{{agent.<attribute>}}An agent attribute — 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 instead for structured values. Common Use Cases: Use Case 1: Session ID in Header
{
  "X-Session-ID": "{{sessionId}}"
}
Use Case 2: Nested Auth Token
{
  "X-Auth-Token": "{{init_response.auth.token}}"
}
Use Case 3: Mixed Static and Dynamic
{
  "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 (one value per case), then reference it in the header. Coval substitutes each test case’s value at run time:
{
  "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:
{
  "role": "tool",
  "content": "result",
  "tool_call_id": "call_123"
}

Responses API

Alternative format for function call outputs:
{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "result"
}
Configure the response format by adding response_format to your model configuration:
{
  "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:
{
  "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?"
    }
  ]
}
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:
{
  "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:
{
  "messages": [...],
  "customData": {...}
}
With wrapper set to "data":
{
  "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.
The input template is not used for the initialization endpoint — that call always uses the 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}})
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:
VariableTypeDescription
{{messages}}ArrayFull conversation history
{{latest_message}}StringMost recent user message content
{{sessionId}}StringSession ID (from init response or simulation ID)
{{simulation_output_id}}StringUnique simulation identifier
{{custom_data}}ObjectThe custom data object
{{custom_data.field}}AnySpecific field from custom data
{{any.nested.path}}AnyExtract any field from init response using dot notation
{{test_case.<attribute>}}AnyA test case attribute — per-test-case state; quote it only when the resolved value is a string
{{agent.<attribute>}}AnyAn agent attribute — 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
{
  "user_input": "{{latest_message}}",
  "session_id": "{{sessionId}}",
  "context": {{custom_data}}
}
Example 2: Nested Init Response Fields
{
  "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
{
  "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.
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.

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:
{
  "messages": [
    {
      "role": "assistant",
      "content": "The response text"
    }
  ]
}
Custom Path Examples: Example 1: Direct Field
Response Message Path: output_message
Extracts from:
{
  "output_message": "The assistant response",
  "metadata": {...}
}
Example 2: Nested Object
Response Message Path: data.response.text
Extracts from:
{
  "data": {
    "response": {
      "text": "The assistant response"
    }
  }
}
Example 3: Array Index
Response Message Path: choices.0.message.content
Extracts from:
{
  "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):
{
  "messages": [
    {
      "role": "user",
      "content": "Hello",
      "timestamp": "2025-01-15T10:30:00Z"
    }
  ]
}
With stripping enabled:
{
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ]
}
Common Error Pattern:
{
  "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:
{
  "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

SymptomLikely causeFix
Failed to run simulation due to an unexpected errorInvalid input template — unquoted string variables, malformed JSON, or bad field referencesCheck the template for valid JSON and wrap string variables in quotes (see quoting rules)
Invalid JSON response from endpointAPI returned a non-JSON responseReturn Content-Type: application/json
Could not extract message from path 'X' in responseResponse message path doesn’t match your response structureVerify the dot-notation path matches your actual response
messages.0.property timestamp should not existAPI rejects timestamp fieldsEnable Strip Message Timestamps
Tool calls not showing in transcriptTool-call extraction not configuredEnsure the API returns OpenAI-compatible format, or contact support for custom extraction
Session ID not working in headersTemplate variable isn’t being substitutedConfirm the init endpoint returns a sessionId field and check custom headers