---
title: "Send one text turn to a prompt-driven hosted agent that has no conversation flow"
method: POST
path: "/v1/agents/{agent_id}/chat-turns"
---

# Send one text turn to a prompt-driven hosted agent that has no conversation flow

`POST /v1/agents/{agent_id}/chat-turns`

## Request

- Base URL: `https://api.skysay.ai`
- URL: `https://api.skysay.ai/v1/agents/{agent_id}/chat-turns`
- Auth: none declared

A text chat test of the agent as a browser voice test would run it, minus audio: the same resolved configuration (system prompt, opening line, language profile, tools, attached knowledge, and for snapshot=draft the editor's unsaved overlay), the same built-in tool prompt appends and the same language directive the voice worker applies, answered by the agent's configured Gemini model. Stateless: send the transcript so far and the new caller_input; caller_input=null with an empty transcript asks for the opening line (the configured first message verbatim, or one generated from the instructions when none is configured). Tool policy: search_knowledge_base runs the real retrieval; end_call ends the chat (ended=true, ended_reason=end_call); every other tool -- send_sms, transfer_to_human, webhook, connection and legacy MCP allowlist tools -- is HELD: the model is told the action did not happen and the call is reported in events with disposition=held. Nothing is persisted and no call record is created. Platform-funded: no wallet reservation and no component charge (only the model ran); metering reports the model calls and tokens this turn consumed. Bounded: 2000 characters per message, 40 transcript lines (the chat ends with ended_reason=transcript_limit), 24000 transcript characters, 4 tool rounds per turn and a 20-second turn budget. Refused with 400 for an agent that has a conversation flow, is not hosted, not active, GPT-Live, bound to a customer-owned model credential, or has no instructions; when no platform model credential is configured; or when the model does not answer in time. An agent WITH a conversation flow is refused with 400 here (a call with it runs the flow, so a prompt-only answer would be one no call gives) and is tested through the workflow run routes instead.

## Path parameters

- `agent_id` string, required

## Request body

- object
  - `caller_input` string, nullable
  - `draft` object: Honoured only with snapshot=draft: {first_message, instructions, language, stack} as the browser test sends them.
  - `language` string: A published language profile locale; defaults to the agent's language.
  - `snapshot` 'published' | 'draft'
  - `test_values` object: The values this test's call variables hold, as a call's call_context would carry them, within its bounds. The turn is admitted as a call is: the opening line and the instructions render with them, and a declared value that is required and missing, or does not match its declared type, refuses the turn with 400 and the admission code (call_variables_missing, call_variables_invalid, call_variables_over_budget). Send the same values with every turn.
  - `transcript` object[]
    - `role` 'caller' | 'agent', required
    - `text` string, required

## Response `200`

{agent_id, snapshot, language, model, reply: {role: agent, text, truncated} | null, events: [{schema, type: tool_call, seq, at_ms, name, arguments, result, ok, disposition: executed|held|ended|refused, duration_ms}], ended, ended_reason: end_call|transcript_limit|null, transcript_messages, limits, tools: {declared, omitted}, metering: {mode: platform_funded, components, model_calls, input_tokens, output_tokens}, duration_ms}

## Other responses

- `400`: Invalid or over-limit request, an agent this test cannot run, no platform model credential, or the model did not answer
- `404`: Agent not found (an agent in another workspace answers exactly as one that never existed)

---

[API](https://skmtc.dev/skysay/apis/skysay-api.md) · [All operations](https://skmtc.dev/skysay/apis/skysay-api/llms.txt) · [OpenAPI document](https://skmtc.dev/skysay/apis/skysay-api/revisions/95b8c66f99aa?raw)
