Agent chat API
Guides

Agent chat API

Send messages to your trained AI agent from your own code and get structured replies in one round-trip.

POST /v1/agent/messages Premium runs one synchronous turn against your AI agent — the same agent, knowledge base, and guardrails that answer on the widget and messaging channels. Use it to put your agent inside your own product: an in-app assistant, a support bot on a channel Asks doesn't cover, or an internal tool.

Each call creates or continues a conversation on the API channel, so every exchange lands in your inbox like any other conversation.

Prerequisites

  • The Premium plan or higher. The rest of the REST API works on Business, but on Business this endpoint returns 403 with code PLAN_LIMIT_REACHED. See Limits & quotas.
  • An API key with the agent:chat scope — a Read & write key has it (Authentication).
  • The agent must be enabled and deployed on the API channel — otherwise the endpoint returns 409. Turn on the API channel at AI Agent → Deploy, or with PATCH /v1/agent and "deployed_channels": { "api": true } (scope agent:write).
  • AI credits. Each reply is charged by the agent's model tier: Essential 1, Pro 4, Ultra 8 credits. Out of credits returns 403 with code PLAN_LIMIT_REACHED.

Calls use the chat rate tier: 20 requests per minute per key, 60 per workspace (see rate limits).

Send a message

Terminal
curl https://api.asks.app/v1/agent/messages \
  -H "Authorization: Bearer $ASKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "What is your return policy?",
    "customer": { "email": "sam@example.com", "name": "Sam" }
  }'
messagestringrequired

The customer's message.

conversation_idstring

Continue an existing API-channel conversation. Omit to start a new one. An unknown id returns 404; a conversation from another channel returns 400.

customerobject

Optional identifying info — email, phone, name, external_id. Used when starting a new conversation to create or match a customer record, so API conversations dedupe against the same person on other channels.

attachmentsarray

Optional attachments passed to the agent.

The response

JSON
{
  "object": "agent_response",
  "conversation_id": "cmc1a2b3c…",
  "messages": ["We accept returns within 30 days of delivery…"],
  "reply": {
    "blocks": [
      {
        "type": "text",
        "text": "We accept returns within 30 days of delivery…",
        "buttons": [{ "label": "Return portal", "url": "https://example.com/returns" }]
      }
    ],
    "quickReplies": ["Start a return", "Talk to a human"],
    "sources": ["Returns & refunds policy"]
  },
  "quick_replies": ["Start a return", "Talk to a human"],
  "buttons": [{ "text": "Return portal", "url": "https://example.com/returns" }],
  "images": [],
  "sources": ["Returns & refunds policy"],
  "escalated": false,
  "credits_used": 1,
  "model_tier": "essential"
}
  • messages — the reply's text, as one or more message bubbles. Render them in order.
  • reply — the full structured reply, the same one the chat widget renders: ordered blocks (text with optional link buttons, products cards with title, price, image and URL, or a standalone image), plus quickReplies and sources. Use it when you want product cards or buttons attached to the right bubble; null when no reply was produced.
  • quick_replies, buttons, and images — flattened shortcuts derived from reply, when the agent produced them.
  • sources — titles of the knowledge base content the reply cited.
  • escalated — true when the agent handed the conversation to a human. Stop expecting AI replies and route the customer to your human channel; the conversation is already flagged in the Asks inbox. Pair this with the agent.escalated webhook to notify your team.
  • credits_used — credits charged for this turn (0 when no billable reply was produced).

messages can come back empty with credits_used: 0 — for example when a human takes the conversation over mid-turn, or abuse protection has blocked it (the first blocked turn returns one closing message, also free). Treat an empty array as "no reply", not as an error.

Multi-turn conversations

Pass the returned conversation_id on the next call and the agent keeps the full history:

JavaScript
const API = "https://api.asks.app/v1";
const headers = {
  Authorization: `Bearer ${process.env.ASKS_API_KEY}`,
  "Content-Type": "application/json",
};

async function ask(message, conversationId) {
  const res = await fetch(`${API}/agent/messages`, {
    method: "POST",
    headers,
    body: JSON.stringify({ message, conversation_id: conversationId }),
  });
  if (!res.ok) throw new Error(`Asks API ${res.status}`);
  return res.json();
}

const first = await ask("Do you ship to Canada?");
const followUp = await ask("How much does it cost?", first.conversation_id);

Store the conversation_id against your own user/session so a returning user continues the same thread.

Verify it works — after your first call, open the inbox: the conversation carries an Agent chat API channel badge, and your message and the agent's reply appear as a normal conversation your team can take over.

Errors to handle

StatusMeaning
400Invalid body, or conversation_id belongs to another channel.
403The plan is below Premium or AI credits ran out (code PLAN_LIMIT_REACHED); the plan doesn't include API access (code forbidden); or the key lacks agent:chat (code insufficient_scope).
404conversation_id not found in this workspace.
409Agent disabled, or the API channel not enabled.
429Chat rate limit hit — retry after Retry-After seconds.

Full request and response schemas are in the Agent reference.

Chat on WhatsApp