Send a message and get the AI's reply
/ai/chats/{ref}/messagesThe primary endpoint. Sends message to the AI in the context of
chat {ref} and returns the assistant's response when complete.
Tools
Tools listed in tools are made available to the AI. Names come from
GET /ai/tools. Omit tools to use the default set (search +
document reading). Send tools: [] to disable all tools.
Images attached by the caller
Pass base64-encoded images in attachedImages. Vision-capable models
(GPT 5.x, Claude Sonnet/Opus, Gemini Pro) read them. With
tools: ["image_generation"] they're also used as edit/composition
inputs ("change the background", "combine these two", etc.).
Images generated by the AI
Generated images are returned in the images array as
{path, url} pairs. The url is a 1-hour signed URL that any
browser <img src> can fetch — no api-key header needed.
Streaming
For long responses you can pre-create a stream via
POST /ai/streams/init, pass streamId here, and poll
GET /ai/streams/{streamId} from a separate connection while this
POST blocks.
Resource grounding
Pass resourceRefs: [123, 456] to attach catalogue resources as
chat documents. Combined with tools: ["document_reading"] (auto-on
when documents are present) the AI can read them via the
document_read tool.
Idempotency
Pass an Idempotency-Key header (a unique random string per logical
send, e.g. a UUID) to make retries safe. The first successful
response with a given key is cached for 24 hours; replays return the
same body byte-for-byte with Idempotent-Replayed: true. Reusing the
key with a different request body returns 422 IDEMPOTENCY_KEY_CONFLICT.
Authorization
apiKey Issued to you when your integration is provisioned. Determines which catalogue resources are visible and which AI tools are available.
In: header
Path Parameters
Header Parameters
Optional client-supplied retry key. 1–255 printable-ASCII characters. Use a UUID or other high-entropy random string.
^[\x21-\x7E]+$length <= 255Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/ai/chats/0/messages" \ -H "Content-Type: application/json" \ -d '{ "message": "Summarise the latest releases in our catalogue.", "tools": [] }'{ "data": { "chatRef": 0, "chatName": "string", "messageIndex": 0, "streamId": "string", "model": "string", "chatResponse": "string", "results": [ { "title": "string", "preview": "string", "resourceId": 0, "score": 0, "rawScore": 0, "matchSource": "metadata", "hits": 0 } ], "reasoning": "string", "images": [ { "path": "/chat_images/1024/gpt_307_stream_xxx.png", "url": "https://example.veristage.com/api/external/ai/images?path=%2Fchat_images%2F1024%2Fgpt_307_stream_xxx.png&exp=1777565000&sig=<signature>" } ] }}{ "success": false, "status": "fail", "message": "string", "error": { "message": "API key is required", "code": "NO_API_KEY" }}{ "success": false, "status": "fail", "message": "string", "error": { "message": "API key is required", "code": "NO_API_KEY" }}{ "success": false, "status": "fail", "message": "string", "error": { "message": "API key is required", "code": "NO_API_KEY" }}{ "success": false, "status": "fail", "message": "Hourly rate limit exceeded.", "error": { "code": "RATE_LIMITED", "message": "Hourly rate limit exceeded." }}