---
title: "Generate the next message in a conversation"
method: POST
path: "/v1/projects/{project_id}/conversations/{conversation_id}/generate"
tags: ["Conversations"]
---

# Generate the next message in a conversation

`POST /v1/projects/{project_id}/conversations/{conversation_id}/generate`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/conversations/{conversation_id}/generate`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Generates the next message using the specified actor's linked agent or chat.
Background by default: returns `202 Accepted` immediately and the reply
lands as a new ConversationMessage when it completes — poll
`GET /v1/projects/{project_id}/conversations/{conversation_id}/messages` for it.
Pass `?wait=true` to block and receive the result inline. On
`completed`, the reply is persisted as a new ConversationMessage
authored by that actor. On `requires_action`, nothing is persisted; the
caller must submit tool outputs via the Agents module and re-invoke
generate — so a flow using client tools should pass `?wait=true`.

## Path parameters

- `conversation_id` string, required

## Query parameters

- `wait` boolean

## Request body

- object
  - `agent_id` string, required: ID of the agent that will produce the next message.
  - `model` string: Optional model override.
  - `stream` boolean: If true, stream tokens via SSE. NOT IMPLEMENTED in v1 — returns 501.
  - `tool_context` object, nullable: Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this generation. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased and keys are never case-converted, so they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.

## Response `200`

Generation completed or requires action (only when `?wait=true`)

- union
  - GenerateConversationMessageCompleted
    - `status` 'completed', required: Indicates generation finished successfully.
    - `content` string, required: The AI-generated text of the reply. This is the canonical field for the assistant's response text.
    - `message` ConversationMessageRecord, required
      - `document_id` string: Document ID
      - `role` 'user' | 'assistant' | 'system': Role of the message sender
      - `actor_id` string, nullable: Optional actor ID associated with this message
      - `agent_id` string, nullable: Optional agent ID that generated this message (set for assistant messages produced by generate)
      - `position` integer: Zero-based position in the conversation
      - `metadata` MetadataBag: Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase. No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.
      - `content` string, nullable: Full text content of the message
      - `idempotency_key` string, nullable: The idempotency key the message was posted with, null when none was sent. A channel that posts each inbound message under its provider's message id finds the transcript row for that message by this key.
    - `generation_id` string, required: ID of the underlying generation record.
    - `trace_id` string, required: Trace ID for observability.
    - `model` string: Model used for generation.
  - GenerateConversationMessageRequiresAction
    - `status` 'requires_action', required: Indicates the agent requires tool-call outputs before it can produce a reply. No message is persisted yet.
    - `generation_id` string, required: ID of the paused generation. Pass to the tool-outputs endpoint.
    - `trace_id` string, required: Trace ID for observability.
    - `required_action` object, required: Tool-call information the client must resolve.
      - `type` 'submit_tool_outputs'
      - `tool_calls` object[]
        - `id` string
        - `tool_name` string
        - `args` object

## Other responses

- `202`: Generation accepted and running in the background (default, when `wait` is omitted or `false`). The reply is persisted as a ConversationMessage when it completes.
- `400`: Invalid request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Conversation or actor not found
- `429`: `QUOTA_EXCEEDED`: an `enforce`-mode generation quota is exhausted. `error.meta` carries `quota_id`, `metric`, `limit`, `window` and `resets_at`, with a `Retry-After` header. Only with `?wait=true`: a background call is answered `202` before the quota is read.
- `501`: Streaming not implemented

---

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