본문으로 건너뛰기

기술 문서는 영어와 루마니아어로만 제공됩니다. 아래는 영어 버전입니다. 루마니아어 버전

Developer documentation

Build on aichat.md — conversational AI agents, on any channel.

One platform, one integration. Put a smart agent on your website with a single line of code, connect Facebook, Instagram, Telegram and WhatsApp, or control everything programmatically through the REST API: agents, tools, conversations, leads, CRM, automations, knowledge and analytics.

5channels: Web · FB · IG · Telegram · WhatsApp
REST + SSEfull response or streaming
3tool types: webhook · code · builtin
RAGknowledge base per agent
Getting started

Quick start

The agent on your website, in three steps:

  1. Create an agent in the aichat.md dashboard and copy its widgetId (a UUID, from Agent → Channels → Chat Widget).
  2. Paste the embed script before </body>.
  3. Or, for your own UI, call POST /widget/chat.
index.html
<script src="https://aichat.md/api/v1/widget/bridge/chatbot-script.js?chatflow_id=WIDGET_ID" defer></script>
i

For programmatic access to agents, channels, conversations or CRM, first obtain a token — see Authentication.

Getting started

Base URL & versions

Base URLhttps://aichat.md/api/v1
FormatJSON on request and response; Content-Type: application/json headers.
WidgetPublic routes under /widget, identified by widgetId. Open CORS.
ManagementRoutes authenticated with Authorization: Bearer <token>.
VersionsAlmost everything is under /api/v1. Only one area also has /api/v2: the dashboard (/api/v1/dashboard and /api/v2/dashboard are identical; use v2).
Getting started

Authentication

There are two levels of access:

◐

Public — widget

The /widget/* routes are public, identified by widgetId. No key in the front end. Access is restricted through the agent's list of allowed origins.

🔑

Authenticated — account

The rest of the API requires a Bearer JWT obtained through login. The same token is accepted both as a header and as the access_token cookie.

i

Programmatic authentication is currently done with a session JWT (login → token). There is no long-lived workspace API key to put in .env (yet) — for server-to-server integrations, reuse the login token and renew it with refresh.

Getting a token

Flow: register → verify-email → login. Endpoints that issue a token return { accessToken, refreshToken }.

POST/api/v1/auth/loginpublic
cURL
curl -X POST https://aichat.md/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "tu@firma.md", "pass": "••••••••" }'

# → 200 { "accessToken": "eyJ…", "refreshToken": "eyJ…" }
# → 202 { "twofa_required": true, "pendingToken": "…" }   if 2FA is enabled
# → 228 { "email": "…" }                                  email not confirmed (a NEW code is resent automatically)
!

Watch the field names — they are not uniform: register requires pass, while login accepts pass or password; verify-email uses verificationCode (not code); only 2FA and reset use code.

Authentication endpoints

Method & routeBodyResult
POST /auth/register{ email, pass }200 { email } — then email verification.
POST /auth/verify-email{ email, verificationCode }{ accessToken, refreshToken }
POST /auth/regenerate-code{ email }Resends the verification code.
POST /auth/login{ email, pass }Tokens · 202 2FA · 228 email not confirmed (the server automatically resends a new code → go straight to verify-email).
POST /auth/2fa/login-verify{ pendingToken, code, remember_device? }{ accessToken, refreshToken }
POST /auth/refresh{ refreshToken } (or cookie)Rotates and returns new tokens.
POST /auth/reset-password{ email }Sends a reset code.
POST /auth/new-password{ email, code, newPassword }{ accessToken, refreshToken } (auto-login).
POST /auth/pass-change 🔒{ password, newPassword }Changes the password; invalidates all sessions.
POST /auth/revokeRefreshTokens 🔒{}Global logout — revokes all of your own refresh tokens (only your account's).

2FA (TOTP) — management (all 🔒 Bearer)

POST /auth/2fa/setup (→ { secret, qrCode }) · /2fa/verify-enable (→ { backupCodes }) · /2fa/disable · /2fa/status · /2fa/backup-codes/regenerate · GET /2fa/trusted-devices · DELETE /2fa/trusted-devices/:id (the device id from the list).

Then send the token on any authenticated route: Authorization: Bearer <accessToken>. Auth error: 401 { message: "🚫 Un-Authorized 🚫" }.

Getting started

Conventions & pagination

Response shape

The convention is not uniform across the platform — check it per area:

ShapeAreas
{ success: true, data: … }widget, conversations, handoff, usage, automation-engine, keywords, dashboard, knowledge, template-library
"bare" object/array (no success)agents, leads, amoCRM (reads), sequences, broadcasts, workflows, analytics, notes, tags, canned-responses

Pagination

Large list endpoints accept page + limit (e.g. /conversations, /leads) or cursor + limit (e.g. /conversations/:id/messages). Where no cap is specified, limit has a safety maximum (e.g. usage/transactions ≤ 500).

Time zones, encryption, ids

Date-times are in the Chișinău time zone (Europe/Chisinau) unless specified otherwise. Agents and the widget use UUIDs; conversations/threads use numeric ids.

Getting started

Errors & rate limits

Response codes

CodeMeaning
200 / 201OK.
202Accepted — asynchronous process (e.g. file indexing) or 2FA required.
228Email not confirmed (at login) — not an error; send the user to the verification screen.
400Invalid request (missing field / wrong format).
401 / 403Unauthenticated / forbidden (missing token, unauthorized origin, disabled agent).
402credits_exhausted.
404Resource / widget does not exist.
409Conflict (e.g. duplicate lead within 24h, number already connected).
422Unprocessable entity (comment moderation rejected, WhatsApp without numbers).
429Too many requests.
430Outside the Meta 24h window (Messenger/IG) — you cannot send a free-form message.
500/502/504Server error / service temporarily unavailable — retry with backoff.
!

The error contract differs between layers. The widget layer returns { success:false, error } + X-RateLimit-* headers + retryInSec. The management layer (authenticated) has a global limiter of 1000 requests / 15 min / IP that returns 429 as plain text with RateLimit-* headers (no JSON). Handle the two differently.

Rate limits — widget

ScopeLimit
Chat / per widget600 / min
Chat / per IP120 / min
Config / per widget · per IP1200 / min · 90 / min
Order / per IP5 / min
History / per IP60 / min

Website widget

Widget installation

Three embed methods. Pick one:

1. Bridge script — recommended (web component)

html
<script src="https://aichat.md/api/v1/widget/bridge/chatbot-script.js?chatflow_id=WIDGET_ID" defer></script>

2. Loader — safe for Google Tag Manager (web component)

html
<script async src="https://aichat.md/api/v1/widget/loader.js?widget-id=WIDGET_ID"></script>

3. Static embed v2 — iframe

html
<script src="https://aichat.md/api/v1/widget/v2/embed.js?id=WIDGET_ID"
  data-position="right" data-color="#0d9c93" data-lang="ro"></script>
!

Methods 1 and 2 mount the <aichat-chat> web component; method 3 is an iframe (launcher button + <iframe>) and accepts the data-id/data-position/data-color/data-lang attributes. CSS selectors on aichat-chat do NOT work with method 3.

The look (color, name, avatar, message) is set from the dashboard or through the widget config API. The snippet can also be generated programmatically with POST /widget/embed-code { chatbot_id } → { success, html }.

Website widget

Conversation — chat

POST/api/v1/widget/chatpublic
FieldDescription
widgetIdrequiredThe agent's UUID (also accepts widget_id).
messagesrequired*[{ role, content }], role = user/assistant.
messagealt.A single message, instead of messages.
sessionIdrecommendedTies messages into a conversation. See the note below.
attachmentsoptionalImages (max 3, ≤4 MB, jpeg/png/webp/gif).
!

sessionId is NOT returned in the response. If you don't send it, consecutive anonymous requests do not share a conversation (no memory). Generate and persist a sessionId YOURSELF (e.g. in localStorage) and send it with every message — it is also the key for the history.

example
curl -X POST https://aichat.md/api/v1/widget/chat \
  -H "Content-Type: application/json" \
  -d '{
    "widgetId": "1d00a200-8ad9-4bb3-a165-7cfa45960e1d",
    "sessionId": "sess-42",
    "messages": [{ "role": "user", "content": "What services do you offer?" }]
  }'

Response (200)

application/json
{
  "success": true,
  "message": "We offer manicure, pedicure and podiatry treatments…",
  "products": [{ "id","title","description","price","currency"?,"image_url"?,"product_url"?,"sku"?,"availability"? }],
  "sources": [{ "index","title","snippet","url"? }],
  "citations": [ … ], "media": [{ "type","url","title" }],
  "actions": [{ "label","url","type" }],
  "quick_replies": [{ "label","value" }],
  "metadata": { "schema": "aichat.rich.v1", "quickReplies", "products", "sources", … } // camelCase duplicate
}
Website widget

Streaming — token by token (SSE)

POST/api/v1/widget/v2/chat/streampublic

The response is Server-Sent Events: each event is a data: <JSON> line, with its type in the type field. Body: { widgetId, messages | message, sessionId?, images? }.

EventPayload
token{ content } — a chunk of text (repeated).
tool_start{ message } — the agent is running a tool.
metadata{ products, quick_replies, sources, citations }.
done{ duration_ms, tokens_in, tokens_out, tools_used, … }.
error{ message } — sent on the stream (HTTP stays 200).
!

On the stream, sources and citations are always empty (reserved), and products have a different shape than on /chat: { title, description, price, image, url, sku, in_stock } — image/url (not image_url/product_url). Don't blindly reuse the /chat renderer.

javascript — reading the stream
const res = await fetch("https://aichat.md/api/v1/widget/v2/chat/stream", {
  method: "POST", headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ widgetId: "WIDGET_ID", sessionId, message: text })
});
const reader = res.body.getReader(), dec = new TextDecoder();
let buf = "";
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });
  const parts = buf.split("\n\n");
  buf = parts.pop();                     // keep the incomplete fragment
  for (const block of parts) {
    if (!block.startsWith("data:")) continue;
    const ev = JSON.parse(block.slice(5));
    if (ev.type === "token") ui.append(ev.content);
  }
}
Website widget

Widget config — public read

The agent's public configuration (name, message, color, avatar, suggestions, products). The canonical endpoint is POST; the GET variant is an alias with ETag/304 caching. For setting the config (owner, with a token) see Agent widget config.

POST/api/v1/widget/configpublic
GET/api/v1/widget/{widget_id}/configpublic

POST requires the body { widget_id }; GET takes widget_id from the path and adds ETag + Cache-Control: max-age=60 (send If-None-Match to get 304). Response: { success, config: { name, primary_color, welcome_message, placeholder, assistant_name, greeting_message, position, starter_prompts[], agent:{name,avatar,tone}, products[], cart_enabled } }.

Website widget

Conversation history

GET/api/v1/widget/history?widget_id=…&session_id=…public

The session's last 40 messages: { success, messages: [{ role, content, time }] }. Both parameters are required.

Website widget

Order

POST/api/v1/widget/orderpublic
FieldDescription
widget_config_idrequiredThe widget identifier (the same UUID as widgetId). Without it → 400.
customer_namerequiredThe customer's name.
cart_itemsrequiredThe products (non-empty list).
total_amountrequiredTotal (> 0, ≤ 1 000 000).
customer_phoneoptional8–15 digits.
customer_address · customer_notesoptionalDelivery & notes.

Success: { success: true, order_id }. Rate limit 5/min/IP; 24h deduplication on phone+widget (409 duplicate_lead_24h).

example — POST /widget/order
{
  "widget_config_id": "WIDGET_ID",
  "customer_name": "Ana Pop",
  "customer_phone": "37360123456",
  "customer_address": "33 Ismail St, Chișinău",
  "cart_items": [
    { "id": "sku-12", "title": "Set California", "price": 180, "qty": 2 }
  ],
  "total_amount": 360,
  "customer_notes": "no wasabi"
}
Website widget

Headless integration — the agent in your UI

Already have a chat on your website and only want the "brain"? You can integrate the full agent into your own interface — without our bundle, without an SDK, without cookies. The whole surface is a public JSON/SSE API, authenticated only by widgetId. In practice you call the same endpoints described above from your own code.

from your UI — a single call
curl -sS https://aichat.md/api/v1/widget/chat \
  -H 'Content-Type: application/json' \
  -d '{
    "widgetId": "WIDGET_ID",
    "sessionId": "user-42-conv-7",
    "messages": [{ "role": "user", "content": "Hi, do you deliver to Chișinău?" }]
  }'
# → { success, message, products, quick_replies, sources, actions, ... }

The pieces you need, all documented above:

PurposeEndpoint
Full responsePOST /widget/chat
Real-time typingPOST /widget/v2/chat/stream (SSE)
Persona for your UI (name, avatar, suggestions)GET /widget/{id}/config
Conversation rehydrationGET /widget/history

Authentication & CORS

  • Auth = widgetId only. No API key, no Bearer, no cookie, no identity HMAC.
  • CORS on /widget reflects any origin (credentials: false) — your domain is accepted by default.
  • The agent's allowed-origins list is checked at application level and is bypassed when there is no Origin header (server-side call). So if you call from your backend, you are never blocked. If you call directly from the browser, ask the agent's owner to add your domain to the allowed origins.
i

Security recommendation: call from your backend (you keep widgetId hidden) and put the verified user's identity in sessionId (e.g. clientUserId:conversationId). There is no cryptographic user↔session binding of the "user_hash" kind (yet).

Memory / session

You own the sessionId (a stable string per user-conversation). The server persists every turn, but does not automatically re-inject the history into the prompt — for multi-turn, send the history yourself in messages[] (max 20 messages, 4000 characters/message, 3 images).

Recommended vs legacy

UseAvoid (old Flowise compat)
POST /widget/chatPOST /prediction/:chatflowId
POST /widget/v2/chat/streamGET /chatflows/:id, /chatflows-streaming/:id
GET /widget/:id/config/public-chatbotConfig/:id, /runtime-config/:id
GET /widget/history/chatmessage/:id, /apif/*

Agents & tools

Agents

Create and manage agents programmatically. All routes require Bearer. An agent has: a name, instructions (system prompt), model, AI provider, tone, channels and config.

RouteDescription
GET /agentsThe account's agents.
POST /agentsCreates an agent. Body: name*, instructions, model, aiProvider, description, tone, answerLength, config.
GET /agents/:idDetails + channels + metadata.
PUT /agents/:idUpdates (name, instructions, model, temperature, config, capabilities, language…).
DELETE /agents/:idDeletes the agent.
POST /agents/:id/toggle-pausePause / resume ({ paused }).
POST /agents/onboarding/parse-websiteExtracts content from a website ({ url }).
POST /agents/onboarding/generate-instructionsGenerates a system prompt.
POST /agents/onboarding/completeCreates the agent from the onboarding config.
i

temperature is locked at 1.0 on creation (any value sent with POST is ignored), but it can be changed later through PUT /agents/:id.

cURL — create an agent
curl -X POST https://aichat.md/api/v1/agents \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "name": "Salon assistant", "instructions": "You are the online assistant…", "model": "gpt-4o-mini", "tone": "friendly" }'

Agent widget config

PATCH/api/v1/agents/:id/channels-widget🔒 Bearer

Atomic merge into channels.widget. Fields: theme_color, persona_name, persona_avatar_url, welcome_message, subtitle, placeholder, starter_prompts (max 4, ≤80 chars), position, enabled, pet_name, show_online_dot, theme_mode, quick_replies_enabled, auto_focus_input, allowed_origins, plus hide_branding and footer_text (PRO+ only). Unified alternative: PUT /agents/:id/widget-config (also accepts legacy camelCase keys).

Agents & tools

Models & providers

An agent has a model (a text name, e.g. gpt-4o-mini) and an ai_provider (default azure). The available values depend on the account's plan — get the actual list with:

GET/api/v1/asistants/models🔒 Bearer

Response: { models, userPlanId }. Use a model from this list with POST /agents / PUT /agents/:id. (There is also the historical alias /asisstants/models, with a double "s".)

i

"agent" = "assistant". The parameter name differs between endpoints: agentId (agents/altegio), assistantId (sheets/knowledge), asistantId (files — a single "s"), assistant_id (chatbots/pause-rules). Check the exact spelling for each endpoint.

Agents & tools

Tools (functions)

Give the agent capabilities — call an API, run code or a built-in function. Bound to an agent. All routes require Bearer.

RouteDescription
GET /agents/:agentId/toolsThe list of tools.
POST /agents/:agentId/toolsCreates. Required: name, description, tool_type (+ conditionally webhook_url / code_body).
PUT /agents/:agentId/tools/:toolIdUpdates.
DELETE /agents/:agentId/tools/:toolIdDeletes.
PATCH /agents/:agentId/tools/:toolId/toggleEnables / disables.
POST /agents/:agentId/tools/attachAttaches a tool from the account's library ({ tool_name } or { flowise_tool_id }).
POST /agents/:agentId/tools/detachDetaches ({ tool_name }).
POST /agents/:agentId/tools/:toolId/testRuns with test arguments.
!

For /test, the arguments must be nested under arguments: body { "arguments": { … } }. Top-level → ignored. Note: name is normalized automatically ([^a-zA-Z0-9_]→_, lowercase) — "Verifică Stoc" becomes verific__stoc.

The three types

🔗

webhook

Calls a URL. webhook_url, webhook_method (default POST), webhook_headers. {{var}} interpolation from the arguments.

⚙

code

Sandboxed JavaScript (code_body; javascript only). Receives args + a minimal fetch (status/ok/headers/json/text, 10s timeout); console disabled, modules blocked (except url), code timeout timeout_ms (default 15s).

◆

builtin

Built-in capability (builtin_type + builtin_config): Google Sheets, Altegio, SMS, Telegram notifications and more.

The parameters field (the arguments seen by the model) is a list:

example — creating a webhook tool
curl -X POST https://aichat.md/api/v1/agents/AGENT_ID/tools \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "name": "check_stock",
    "description": "Checks a product's stock by code",
    "tool_type": "webhook",
    "webhook_url": "https://api.shop.md/stock?sku={{sku}}",
    "webhook_method": "GET",
    "parameters": [{ "name": "sku", "type": "string", "required": true }]
  }'

Conversations & Inbox

Conversations

The unified inbox across all channels. All routes require Bearer. Responses { success, data }.

RouteDescription
GET /conversationsList (query platform, search, tab, page, limit).
GET /conversations/:idOne conversation + recent messages.
GET /conversations/:id/messagesPaginated messages (cursor, limit).
POST /conversations/:id/messagesSends a human reply ({ content }). 502 if delivery on the channel fails.
PUT /conversations/:id/tagsOverwrites the tags ({ tags }).
POST /conversations/:id/readMarks as read.
GET /conversations/:id/searchSearches the conversation's messages (?q=).
GET /conversations/media-proxy?url=FB/IG media proxy through the origin.
GET /conversations/:id/refresh-avatarRefreshes the profile picture (Meta Graph) when the signed CDN URL expires → { success, profile_pic, name }.
Conversations & Inbox

Human handoff

Moves a conversation from the bot to a human operator and back. Bearer, responses { success, data }.

GET /handoff/pendingConversations waiting for a human.
GET /handoff/mineMy conversations.
POST /handoff/:id/takeTakes over the conversation.
POST /handoff/:id/resolveResolves ({ notes? }).
POST /handoff/:id/returnHands control back to the bot.
POST /handoff/:id/stop-bot · /start-botStops / restarts automatic replies.
POST /handoff/:id/cancel-auto-pauseCancels an active automatic pause.
Conversations & Inbox

Notes · Tags · Canned · Export · Contacts · Pause-rules

Inbox tools. All Bearer.

AreaEndpoints
Notes /notesGET /:threadId · POST / ({ threadId, content }) · PUT /:id · DELETE /:id (both only by the note's author; otherwise 403).
Tags /tags, /threadsGET/POST /tags · DELETE /tags/:id · GET /threads/:threadId/tags · POST /threads/:threadId/tags ({ tagId }) · DELETE /threads/:threadId/tags/:tagId.
Canned responses /canned-responsesGET · POST ({ shortcut, title, content }) · PUT /:id · DELETE /:id.
Export /exportGET /:threadId?format=json|csv · GET /:threadId/pdf (print-ready HTML).
Contacts /contactsGET /duplicates · POST /merge ({ primaryThreadId, secondaryThreadId }).
Pause rules /pause-rulesGET /defaults · GET/POST /config · GET/POST / (per assistant_id).
Web push /pushPOST /subscribe · /unsubscribe · GET /status · GET /vapid-key (public).
Bulk inbox /threads-bulkPOST /bulk-action ({ threadIds[], action }; action ∈ close|pause|unpause|mark_spam|export).
Conversations & Inbox

Meta templates

Sends structured templates on Messenger (proxy to Facebook Graph). Bearer; all require recipient_id and return { success, message_id }.

POST /templates/receiptReceipt template (order_number, summary, items[]…).
POST /templates/listList template (elements ≥ 2).
POST /templates/mediaMedia template (url or attachment_id).
POST /templates/otn/request · /otn/sendOne-Time Notification: request permission, then send with the token.

Template library (CRUD) — /templates/library

GET /library · GET /library/most-used · GET /library/variables · POST /library/validate · GET /library/:id (+/preview) · POST /library ({ name, templateType, content }, types: text/quick_replies/buttons/generic/carousel/media) · PUT /library/:id · POST /library/:id/duplicate · DELETE /library/:id.


Channels

Facebook Messenger

A page is connected through OAuth from the dashboard; the API binds a page to an agent. All require Bearer.

GET /facebook/oauth-initThe Facebook OAuth dialog URL.
GET /facebook/pagesThe pages + which agent each one has (pages[].assigned.chatbotId).
POST /facebook/pages/assignBinds a page to an agent ({ pageId, agentId }) + subscribes the webhook.
DELETE /facebook/pages/unassignDetaches ({ chatbotId }, not pageId!). Non-destructive.
POST /facebook/send-messageMessage as an operator ({ receiver_user_id, text, reply_to_mid? }; pauses the bot). 430 outside the Meta window.
POST /facebook/send-attachmentAttachment (multipart file + receiver_user_id).
!

unassign is done by chatbotId (from GET /pages → assigned.chatbotId), NOT by the pageId used for assign. The same rule applies to Instagram.

Channels

Instagram

Instagram DM uses Instagram Login and a professional account. Do not confuse DM authorization with the separate Facebook Login publishing flow. Authorize only your own assets; if permission is denied or the session expires, restart connection from the dashboard.

All require Bearer.

GET /instagram/login · /pagesOAuth URL · IG accounts + assignment.
POST /instagram/pages/assignBinds an IG account to an agent ({ instId, agentId }).
DELETE /instagram/pages/unassignDetaches ({ chatbotId }).
POST /instagram/pages/revokeDeletes the connection locally (requires { instId }; does not revoke the OAuth grant on Meta).
POST /instagram/unlinkAccount-level disconnect.
POST /instagram/send-message · /send-attachmentMessage / attachment as an operator.
Channels

Telegram

Shared bot (@aichat_connect_bot) — the customer does not enter a bot token. Connection: 6-digit code → in the bot → Telegram Business Connection (Premium) → business_connection_id. All require Bearer.

POST /telegram/get-codeGenerates the connection code ({ success, code }).
GET /telegram/business-connectedStatus ({ connected, business_connection_id }).
POST /telegram/unlink · /send-messageDisconnect · message as an operator.
Channels

WhatsApp (Cloud API)

Pilot access. General onboarding is not open. External customer access depends on Meta permissions and approval, number readiness and an actual delivery test. A ready connection does not prove that the recipient received a message.

Connect in the dashboard

  1. Create and activate an agent.
  2. Open WhatsApp and start Embedded Signup if pilot access is available.
  3. Authorize your own business, WABA and phone number. Do not paste access tokens into the dashboard.
  4. Complete any verification and registration required by Meta; check the webhook subscription and assigned agent.
  5. Send an inbound message from a dedicated authorized test phone and confirm the reply on the phone, not only in the inbox.

You need the required rights to the selected assets. A number can belong to only one aichat.md account. Meta test numbers can send only to recipients allowed in their test list.

Authenticated API

Prefix: /api/v1; valid session or Bearer. Onboarding operations remain gated by pilot access.

GET /whatsapp/configAvailability, app, configuration and signed attempt session valid for 10 minutes.
GET /whatsapp/agentsThe account's active agents.
POST /whatsapp/connectRequires code, phone_number_id, waba_id, agentId, attempt; pin when registration requires it. The session is consumed once before code exchange.
POST /whatsapp/completeResume incomplete setup with phone_number_id, agentId and pin when required.
GET /whatsapp/statusConnections, agent, subscription and missing conditions. Historical verification is not a fresh token validation on every request.
POST /whatsapp/agentChange the agent on your connection: phone_number_id, agentId.
POST /whatsapp/disconnectDisable the local connection: phone_number_id. Revoking Meta access is a separate action.
GET /whatsapp/numbers/:phoneId/templatesTemplates for your own connection.
GET /whatsapp/eventsEvents needing attention; a retry may be refused when delivery could be duplicated.
POST /whatsapp/connect-manualRetired: 410 use_embedded_signup. There is no pasted-token fallback.

Messages and operator handoff

Free-form messages are restricted to the 24-hour window from the customer's last message. Outside that window, use an approved template and respect recipient opt-in. Operator takeover does not extend this window. Returning to AI must be intentional. Check agent pause and disconnection before resuming sends.

Meta acceptance, sent, delivered, read and failed are distinct states; read receipts also depend on recipient settings. AI-generated text is not delivery proof. Calling, coexistence and campaigns are not presented here as available features.

Troubleshooting

  • 503 whatsapp_rollout_pending: the account has no pilot access.
  • 401 signup_session_expired or 409 signup_session_already_used: restart connection for a fresh session.
  • 409 whatsapp_number_already_connected_to_another_account: the asset belongs to another account.
  • phone_verification_required / registration_pin_required: complete the indicated Meta step.
  • Meta 131030: the recipient is not allowed for this test number. Do not automatically send to a different number.
  • whatsapp_template_required: the free-form messaging window has closed.

Conversations are retained in the inbox. See the privacy policy and data deletion procedure. Disconnecting a channel does not automatically erase history.

Channels

Meta webhooks

For FB/IG/WhatsApp, messages come in through the Meta webhook — configured automatically on connection. You don't host anything.

GET /api/v1/webhook publicMeta verification (hub.challenge + verify-token).
POST /api/v1/webhook publicReceives events (signed with X-Hub-Signature): messages, comments, reactions, statuses.
Channels

Messenger / Instagram profile

Configures a page's messaging profile: "Get Started" button, persistent menu, greeting message, ice-breakers. /messenger-settings, Bearer. :platform = facebook or instagram.

GET /messenger-settings/:platform · PUT /:platformReads / saves the profile settings.
POST /messenger-settings/:platform/get-startedThe "Get Started" button.
POST /messenger-settings/:platform/persistent-menuPersistent menu.
POST /messenger-settings/:platform/greetingGreeting message.
POST /messenger-settings/:platform/ice-breakersIce-breakers (suggested questions on open).
GET /messenger-settings/:platform/live-profileThe current profile, straight from Meta.

Automations

Automation engine

Event-driven rules (trigger → conditions → actions). Bearer, responses { success, data }.

POST /automation-engine/triggersCreates a trigger. E.g.: { integrationId, name, triggerType:"new_conversation", triggerScope:"dm", allowedActions:["reply_text"] }. triggerType ∈ new_conversation|story_mention; triggerScope ∈ dm|story.
GET /automation-engine/triggersList (query integrationId).
PUT · DELETE /automation-engine/triggers/:idUpdates / deletes.
POST /automation-engine/previewDry run, no side effects.
GET /automation-engine/executionsExecution history.
i

There is also an older scheduled tasks module (AI/fixed follow-up messages) mounted at /api/v1/tasks: GET/POST /tasks, POST /tasks/update, /tasks/delete, /tasks/switch.

Automations

Sequences & Broadcasts

Sequences (drip) — /sequences

GET / · GET /:id · POST / ({ name, steps[] }) · PUT /:id · DELETE /:id · PATCH /:id/activate · POST /:id/enroll ({ recipientId, pageId }) · GET /:id/enrollments.

Broadcasts (one-to-many) — /broadcasts

GET / · POST / ({ message | templateId, filterTags?, scheduledAt? }) · GET /:id · POST /:id/send (queues) · DELETE /:id. Delivery is worker-driven; /send only changes the state to sending.

Automations

Keywords & Workflows

Keywords (keyword auto-reply) — /keywords

GET / · GET /:id · POST / · PUT /:id · PATCH /:id/toggle · DELETE /:id · POST /match-preview ({ integrationId, text }).

On POST /: only keyword is required. matchType ∈ exact|contains|starts_with|regex (default contains); responseType ∈ dm|comment|both (default dm).

!

dmMessage is required when responseType is dm (the default!) or both; commentReply is required when it is comment or both. A minimal POST { keyword } → 400 "dmMessage is required for dm/both response types".

Workflows — /workflows

GET / · GET /:id · POST / ({ name, steps[], triggerConfig }) · PUT /:id · DELETE /:id · PATCH /:id/activate.

Automations

Scheduled messages

/schedule, Bearer. POST /create ({ chat_id, name, description, dateTime, service } + repeat?) · POST /update ({ id, … }) · POST /delete ({ id }) · GET /by-conversation?chat_id=&service=.


Leads & CRM

Leads & tags

Conversations become leads, with tags and a kanban board. Bearer.

GET /leadsList with filters (channel, tags, score, date, search, page).
GET /leads/canban · /leads/canban/tagKanban board · column per tag. (the route is literally spelled canban.)
POST /leads/create-tag · GET /leads/tagsCreates a tag ({ tag, description, color? }) · list.
POST /leads/set-tagSets a tag ({ tag_id, tag_name, chat_id, type, service }).
POST /leads/pausePauses the bot on a lead ({ chat_id, service }).
POST /leads/analyzeAI analysis ({ page_id, recipient_id, platform }).
GET /leads/meta · /leads/meta/statsEnriched Meta leads + statistics.
Leads & CRM

amoCRM

Synchronization with amoCRM. Bearer. Reads return "bare" objects ({pipelines}, {leads}); writes return { success }.

GET /amocrm/statusConnection status.
POST /amocrm/connect · /connect-oauth/connect: { subdomain, access_token } required (client_id/client_secret optional). /connect-oauth: { code, subdomain, client_id, client_secret } required.
POST /amocrm/logoutDisconnect.
GET /amocrm/pipelinesPipelines (the statuses come in _embedded.statuses).
GET /amocrm/statuses?pipeline_id=Statuses — pipeline_id required.
POST /amocrm/statusesSaves the pipeline + selected statuses.
GET / POST /amocrm/leadsList / create leads.
GET /amocrm/leads/:id · PATCH /leads/:idDetail · update (it is PATCH).
POST /amocrm/leads/:id/move · /note · /tagsMove · note ({ text }) · tags ({ tags_to_add, tags_to_delete }).
POST /amocrm/tasksTask on a lead ({ lead_id, text }).
GET /amocrm/custom-fields · /usersCustom fields · users (for mapping + responsible_user_id).
PATCH /amocrm/contacts/:idUpdates a contact (e.g. { custom_fields_values }) — useful for filling in the phone/email after creation.
Leads & CRM

Bitrix24

CRM sync — POST /bitrix-crm/connect ({ webhookUrl }) · GET /bitrix-crm/status · GET /bitrix-crm/statuses · DELETE /bitrix-crm/disconnect. All Bearer.

Chat channel (Bitrix24 Open Channels) — separate from the CRM sync, registers an imbot at /bitrix: POST /bitrix ({ data: { botName, clientId, webhookUrl } }) · POST /bitrix/remove · GET /bitrix/getDataBitrix.


Native integrations

Google Sheets

The agent writes leads as rows in a sheet. On connection a builtin tool is attached. Bearer.

GET /google-sheets/connect-infoThe robot email you share the sheet with.
POST /google-sheets/test-connectionChecks access.
POST /google-sheets/connectConnects + attaches the tool ({ assistantId, spreadsheetUrl, fields, sheetTab?, dedupeKey? }).
GET / · /for-assistant/:idList · status per agent.
PATCH /:id · POST /:id/test-row · DELETE /:idEdit mapping · test · disconnect.
Native integrations

Altegio — online booking

Connects a salon's Altegio calendar. The customer only pastes the Company ID; 5 tools are attached (services, availability, create, cancel, reschedule). Bearer.

POST/api/v1/altegio/connect🔒 Bearer
FieldDescription
agentIdrequiredThe agent the tools are attached to.
companyIdrequiredThe salon's ID in Altegio.
userTokenoptionalEnables cancelling/rescheduling. Stored encrypted.
bookingEnabledoptionalAllows creating bookings.

Response: { integrationId, companyId, salon, servicesCount, bookingEnabled, attached }. Status: GET /altegio/for-assistant/:agentId; disconnect: DELETE on the same route.

Native integrations

Telegram Leads · SMS · other integrations

IntegrationEndpoints
Telegram Group Leads /telegram-leadsGET /connect-info · GET / · POST /start-link · GET /status?assistantId= · GET /for-assistant/:id · PATCH /:id · POST /:id/test · DELETE /:id.
SMS (Infobip)Exposed as a builtin tool (builtin_type: "sms_send"), not as a REST route — the key stays on the server.
Shopify /shopifyPOST /logout (disconnect).
Wix /wixPOST / (connect) · POST /logout.
Jivo /jivoPOST / ({ data: { providerId } } — providerId required) · POST /remove · GET /getDataJivo.

Knowledge

Knowledge base (RAG)

Feed the agent with sources — text, documents, websites, Q&A pairs. Bearer, responses { success, data }.

GET /knowledge/sources · /sources/:idList (filters assistantId, status, sourceType) · a single one.
POST /knowledge/sourcesCreate. Required: sourceType (∈ text|document|website|qa_pairs) + name. content only for text; websiteUrl only for website; document/qa_pairs require neither.
POST /knowledge/sources/uploadFrom an uploaded file (≤10 MB).
PUT /knowledge/sources/:id · DELETE /:idUpdate (re-chunk) · delete.
POST /knowledge/sources/:id/resync · /reextract-productsRe-chunk/re-embed · re-extract products.
GET /knowledge/sources/:id/products · /chunksRead the extracted products (pairs with re-extract) · the source's (re-)generated chunks.
GET / PUT /knowledge/config/:assistantIdPer-agent RAG config.
Knowledge

Files & documents

Documents in an agent's index (pdf, doc, docx, txt, csv, json, md, markdown, rtf, log, text; ≤5 MB/file). Unlisted extensions are attempted, not rejected — only a missing extension → 400. Bearer.

POST /filesUpload (multipart field files + asistantId). Automatically async if one file is ≥300 KB or the total is ≥700 KB (or forced with asyncUpload=true): 202 + jobId; below the thresholds: 200 sync.
GET /files/upload-status/:jobIdIndexing progress.
GET /files?asistantId= · POST /files/deleteList · delete ({ fileId, asistantId }).
Knowledge

Website scraping → searchable index

Extracts a website's content and makes it searchable (pages, products). /scrape, Bearer.

POST /scrapeStart a scrape ({ url, selector_url_override?, sitemap_urls_override_str? }).
GET /scrape/search?query_text=&page_size=&page_number=&index=Paginated full-text search (all parameters required).
GET /scrape/list · /status/:indexScraped websites · a job's status (404 if missing).
GET /scrape/:index?offset=&limit=Rows/products in an index (default offset 0, limit 100).
PUT /scrape/:index · DELETE /:indexUpdate an entry ({ key, data }) · delete an entry ({ key }).
DELETE /scrape/delete/:indexDelete a website's entire index.

Analytics & voice

Analytics & Dashboard

Analytics — /analytics (Bearer; "bare" responses)

GET /messages-per-day?days= ([{ date, incoming, outgoing }]) · GET /top-agents?days= · GET /overview ({ messagesToday, messagesThisWeek, activeThreads, total_contacts, … }).

Dashboard — /api/v2/dashboard (recommended) or /api/v1/dashboard

All GET, Bearer, short cache, rate limit 60/min: /stats?period= · /trend?days= · /pages · /leads/recent?limit= · /alerts · /comments-stats?period= · /cost-detail?period=.

Analytics & voice

Usage & credits

/usage, Bearer. Query period = 1d/7d/30d/all (default 30d).

GET /usage/summary?period={ credits:{ remaining, limit, used_in_period, used_today }, messages, tokens, cost_usd, wallet }.
GET /usage/breakdownBy model, source, agent, day.
GET /usage/transactionsCredit transaction ledger (limit ≤ 500).
GET /settings/credit-historyCredit history + subscription + 30-day chart.
Analytics & voice

Voice (ElevenLabs)

Voice cloning and management. /create and /update use multipart/form-data. Bearer.

GET /eleven-labs · /voice?eleven_id=List voices · one voice.
POST /eleven-labs/createCreate a voice — multipart, fields files (repeatable) + name required; optional description, labels (JSON).
POST /eleven-labs/updateEdit a voice (multipart): eleven_id + name required; files optional (present → adds samples; absent → metadata only).
POST /eleven-labs/deleteDelete a single sample from a voice ({ eleven_id, sample_id }).
POST /eleven-labs/voice/deleteDelete the whole voice ({ id }).
GET /eleven-labs/available-voicesVoices available to the account.
!

The multipart field name is exactly files (repeated for multiple samples). Sent literally as files[], multer rejects it with an "Unexpected field" error.


Account & billing

Credits — how they are calculated

The credit is the billing unit. The number of credits for an AI response depends ONLY on the model used — not on how many tokens were consumed. It is a flat per-message charge, per model.

formula
credite_pe_mesaj = pret_flat_al_modelului  (+ suprataxe media, doar FB/IG)

Tool calls and RAG/knowledge add no credits — a message with 5 tool calls costs the same as a simple one.

Price per model

Credits / messageModels (examples)
1GPT-4o mini, 4.1, 4.1 nano, DeepSeek V3, Gemini Flash Lite
24.1 mini, 5 nano, Llama 3.3 70B / Llama 4 Maverick, Grok 3 mini, Gemini Flash
35.4, 5.4 mini, 5 mini, 5.5 mini, Kimi K2.5
4GPT-5, 4o, GPT-OSS 120B, Cohere Command A
5GPT-5.1, Mistral Large 3, Grok 4 Non-Reasoning
8GPT-5.2, Gemini 3 Pro / 3.1 Pro
10–12GPT-5.3, GPT-5.5, Grok 4 fast, 5.6 Terra/Luna
25Grok 4

Media surcharges (FB/IG only)

On top of the model price: incoming voice message (transcription) +1; image generation +5/image; ElevenLabs TTS voice reply +20 (other TTS/STT +10).

Plans & limits

PlanPriceIncluded credits
— (no subscription)00 (+ initial credit balance at sign-up)
standard€492.000
pro€15010.000
ultra€29930.000
business€49950.000
  • Balance = limit − current (from UserSettings.tokens).
  • On subscription activation: limit = max(limit, current + credite_plan).
  • Top-up one-time: credite = floor(tokeni_cumpărați / 3).
  • When exhausted (current ≥ limit) → 402 credits_exhausted.
  • New accounts start with an initial credit balance; without an active subscription there are no recurring credits.
i

Example: an FB message with an agent on Grok 4 = 25 credits; an IG voice message with gpt-5.4 (3) + transcription (+1) + ElevenLabs TTS reply (+20) = 24 credits; a plain text = between 1 and 25 credits, depending on the model.

Reading the status (balance / usage)

The read endpoints are documented in Usage & credits (GET /usage/summary, GET /settings/credit-history) and Billing (GET /stripe/billing/overview, GET /stripe/billing/dashboard).

Account & billing

Account & profile

/users, Bearer.

GET /users/profileFull profile: user, plan_id, remaining credits, Stripe status, subscription + days to expiry, IG/FB channels.
POST /users/updateUpdate full_name, bio, email (also synced to Stripe).
POST /users/change-localeUI language (locale).
POST /users/me/delete-request · /me/delete-cancelGDPR: account deletion request (30-day grace period) / cancellation. delete-request requires { confirm_email } (= the account email, otherwise 400); reason optional → { success, scheduled_at, grace_ends_at }.
Account & billing

Billing (Stripe)

/stripe, Bearer. (The /stripe/webhook webhook is public/signed — you do not call it.)

POST /stripe/create-checkout-sessionSubscription checkout ({ product } = lookup_key, e.g. pro_month).
POST /stripe/create-checkout-session-on-tokenCredit top-up checkout — body { unit_amount_decimal } (amount in USD; grants amount×100 credits).
GET /stripe/create-billing-portalStripe billing portal link.
GET /stripe/billing/overview · /stripe/billing/dashboardPlan + credits + forecast.
GET /stripe/billing/invoices · /billing/charges · /billing/upcomingInvoices · payments · upcoming invoice.
POST /stripe/billing/invoices/:id/retryRetry payment of an invoice.
GET /stripe/historyPurchase history.
Account & billing

Chatbots — channel ↔ agent mapping

A "chatbot" links a channel (FB page, IG, widget…) to an agent. /chatbots, Bearer.

GET /chatbotsList.
POST /chatbots/create · /updateCreate / update a mapping (platform ↔ assistant_id).
POST /chatbots/disable · /deleteToggle enabled · delete.
POST /chatbots/settings · /commentsBulk settings (auto-reply, name, agent) · comment replies.
POST /chatbots/master-toggle · GET /master-statusGlobal on/off for all channels · status.
POST /chatbots/service-bulk-toggle · GET /service-statusOn/off per service · counters.
Account & billing

Teams & referral

Teams (multi-user) — /teams

Multiple operators on one account, with roles (admin/agent/viewer). Bearer.

POST /teams · GET /teamsCreate a team ({ name } — only one owned team per user; a 2nd → 400) · list teams (owned + member).
GET /teams/:id · PUT /teams/:idDetails + members · rename (owner).
POST /teams/:id/inviteInvite by email ({ email, role }).
PUT /teams/:id/members/:memberId/role · DELETE /members/:memberIdChange role · remove member.
POST /teams/accept-inviteAccept pending invitations.

Referral — /referrals

Referral program with promo codes and a Stripe Express account. Bearer.

GET /referrals/referrals-infoDashboard: balance, promo codes, number of referrals, earnings.
POST /referrals/create-stripe-account · GET /dashboardStripe Express account + coupon · dashboard/onboarding link.
POST /referrals/create-promocodeCreate a promo code on your coupon ({ code }).
POST /referrals/deactivate-promocodeDeactivate / reactivate a promo code — reversible toggle ({ code }).
POST /referrals/delete-promocodeDeletes the code permanently (removes it from the list + Stripe active:false; irreversible).
POST /referrals/create-checkout-sessionSubscription checkout with a referral code.

Other APIs

999.md (Simpals)

Integration with the 999.md marketplace. /trei9, Bearer.

POST /trei9/auth · /unlinkLink the account ({ username_simpals, refreshToken }) · disconnect.
GET /trei9/threads · /thread?chat_id=Contacts · a thread's messages.
POST /trei9/send-message · /edit-configSend a message · per-thread config (AI on/off).
Other APIs

Forms & support

/storage. Support tickets and forms (some public, for contact pages).

POST /storage/help-requests 🔒Support ticket + attachments (Telegram notification).
POST /storage/callback 🔒Request a callback.
POST /storage/contact-us · /get-demo · /cv · /enterprise publicPublic contact/demo/CV/enterprise forms. Rate limit 1/10min only on /get-demo and /cv; /contact-us and /enterprise have no limit.
Other APIs

Agent quality (audit)

Automatic audit of an agent's conversations, with scores and recommendations. Bearer.

GET /agents/:id/quality · /quality/:auditIdRecent audits · detail (scores + excerpts).
POST /agents/:id/quality/runRun an audit manually.
POST /agents/:id/quality/:auditId/applyAdds the recommendations to the agent's instructions.
Other APIs

Events & monitoring

GET /events?userId= publicReal-time SSE stream per user (new messages, notifications; 10s heartbeat).
GET /monitoring?start=&end= 🔒Per-user usage: tokens, daily, TTS/STT voice, images.
Other APIs

Audit log · Changelog · Public content

GET /audit?page=&limit=&action=&entity_type=&from=&to= 🔒Your account's action log (paginated, filterable). GET /audit/actions = the action types.
GET /changelog · /latest · /seen 🔒The product's news feed + the "seen" state. POST /changelog/seen marks it as read.
GET /posts/news · /news/:slug · /blogs · /blogs/:slug publicPublic news/blog content (marketing site) — ?locale= parameter.

Comments & social

Page posts (Facebook & Instagram)

Publish and schedule content on connected pages. Bearer; proxy to Meta Graph.

Facebook — /page-posts

POST /page-postsText/link post (message OR link; optional published, scheduled_publish_time between 10 min and 6 months).
POST /page-posts/photoPhoto post (url; optional message, scheduled_publish_time).
PUT /page-posts/:postId · DELETE /:postIdEdit the text · delete.
GET /page-posts?limit=&after=List feed posts (limit default 25, max 100).

Instagram — /ig-publish

POST /ig-publish/postImage post (image_url; optional caption, location_id, user_tags).
POST /ig-publish/video · /reelFeed video / Reel (video_url; optional caption, thumb_offset, share_to_feed).
POST /ig-publish/carouselCarousel of 2–10 items (items[]; optional caption).
POST /ig-publish/storyStory (image_url OR video_url).
i

Native IG scheduling is unreliable (Meta often ignores it) — for IG, publish at the desired time from your own scheduler.

Comments & social

FB/IG comments

Moderation and AI auto-reply on Facebook/Instagram comments, with an approval queue, templates and trigger rules. /comments, Bearer.

Settings & moderation

GET /comments/settings · PUT /settingsPages + auto/private reply toggles · update the toggles.
GET /comments/health · /diagnosticsPage health diagnostics (token, 30-day stats).
GET /comments/recentThe latest 50 comments across all pages.
POST /comments/:id/hide · /reply · DELETE /:idHide · manual reply (≤8000) · delete.
POST /comments/bulk-hideBulk hide/unhide (max 25).
POST /comments/media/:mediaId/toggle-commentsEnable/disable comments on an IG post ({ enabled, page_id }).
POST /comments/settings/:chatbotId/archive · /restoreArchive / restore a page in the settings list.

AI approval queue

GET /comments/pending · /pending/countPending AI drafts + counter for the badge.
POST /comments/:id/approve · /:id/rejectApprove & publish (optional edited_text) · reject.
POST /comments/chatbots/:id/comments-configMode (off/auto/approval) + AI instructions per chatbot/agent.

Templates & trigger rules

/comments/predefined-replies (CRUD)Public reply templates.
/comments/predefined-pm-messages (CRUD)Private message templates.
/comments/trigger-rules (CRUD)Auto-reply rules (matchType exact/contains/regex → reply/PM).
POST /comments/trigger-rules/match-previewTest a rule on a text, without saving.

Live comments (FB Live)

GET /live-comments/:videoId/streamSSE stream of live comments (Graph polled every 2s).
GET /live-comments/:videoIdRecent live comments (non-stream, with an after cursor).
POST /live-comments/:id/hide · DELETE /:idHide · delete.

Reference

Support

◈

aichat.md dashboard

Create the agent, get the widgetId, configure channels and allowed origins.

Open the dashboard →
✉

Technical contact

For access, dedicated integrations or API questions — write to us via the form in the dashboard or POST /storage/contact-us.

◆

Turnkey integration

A complex scenario (custom tools, multiple channels, CRM)? The team will help you.

Programmatic bug reporting: POST /api/v1/bug-report — multipart with up to 10 screenshots (screenshots), rate limit 5/min; attaches the user if you send a JWT, otherwise anonymous.