Developers

CustGPT REST API

Chat with your assistants, feed them knowledge and pull leads into your own systems with a simple JSON API.

Authentication

Create a key under Dashboard → Account → API keys. Send it as a bearer token on every request. Keys carry your account’s full access, so keep them server-side.

curl /api/v1/bots \
  -H "Authorization: Bearer cg_live_your_key_here"

Base URL: /api/v1 · All requests and responses are JSON.

Errors

Failed requests return a non-2xx status and a body like {"error": "Chatbot not found."}.

StatusMeaning
400Something in the request is missing or invalid.
401Missing or invalid API key.
402Your plan limit has been reached (e.g. sources).
404The chatbot or conversation doesn’t exist in your account.

Chat

POST/bots/{botId}/chat

Send a visitor’s message and receive the assistant’s answer. Actions, human handoff and plan limits work exactly as they do in the website widget.

FieldTypeDescription
messagestringRequired. The user’s message (max 2,000 characters).
conversationIdstringContinue an existing conversation. Omit to start a new one.
userIdstringYour own identifier for the end user. Required to continue a conversation.
streambooleanStream the reply as server-sent events (see below).
curl -X POST /api/v1/bots/b_123/chat \
  -H "Authorization: Bearer $CUSTGPT_KEY" -H "Content-Type: application/json" \
  -d '{"message": "Do you ship to Canada?", "userId": "crm-4821"}'

{
  "conversationId": "cv_Q8r7s6",
  "userId": "crm-4821",
  "messageId": "m_T5u4v3",
  "answer": "Yes! Orders to Canada arrive in 4–7 business days [1].",
  "sources": [{ "n": 1, "title": "Shipping information", "url": "https://…" }],
  "humanHandling": false
}

humanHandling: true means a team member has taken over this conversation. The AI stays silent and your teammate replies from the dashboard.

Streaming responses

With "stream": true the response is text/event-stream. Each data: line is JSON with a type:

typePayload
startconversationId, userId
deltatext: the next piece of the answer
actionname, label, status (running · done · error) when the assistant calls one of your Actions
donemessageId, the full answer, sources, humanHandling

Chatbots

GET/bots

List your chatbots with their settings and stats.

GET/bots/{botId}

Get a single chatbot.

Knowledge sources

GET/bots/{botId}/sources

List the pages, files and text a chatbot has learned from.

POST/bots/{botId}/sources/text

Add a block of text. Body: {"title": "Refund policy", "content": "…"}. It’s searchable immediately.

POST/bots/{botId}/sources/qa

Add a custom answer that takes priority for matching questions. Body: {"question": "…", "answer": "…"}.

POST/bots/{botId}/sources/crawl

Import web content in the background. Body: {"url": "https://example.com", "mode": "crawl" | "sitemap" | "single"}. Returns 202 with a crawlId.

Conversations

GET/bots/{botId}/conversations?limit=50

Most recent conversations across all channels (website, Telegram, API).

GET/bots/{botId}/conversations/{conversationId}

A full transcript including AI answers, team replies (role: "agent") and sources.

Leads

GET/leads

Every captured lead across your chatbots, newest first: email, name, phone, status (new · contacted · qualified · won · lost), notes, bot_name, captured_at.

Webhooks

Prefer push over polling? In a chatbot’s Integrations tab, add a webhook URL. We POST JSON for these events:

EventWhen
lead.createdA visitor shares their contact details.
handoff.requestedA visitor asks to talk to a person.
conversation.startedThe first question and answer of a new conversation (opt-in).
testYou pressed “Send test”.
{
  "event": "handoff.requested",
  "bot": { "id": "b_123", "name": "Acme Help" },
  "data": { "conversationId": "cv_Q8r7s6", "reason": "Wants to change a delivery address", "channel": "web" },
  "sentAt": "2026-09-26T09:30:00.000Z"
}