. Create an API key under API keys.API reference · Conversations
AI assistant
AI replies from the business information, knowledge documents and web pages, saved answers, questions the assistant could not answer, the AI report, and the inbox helpers (suggested reply, chat summary). Changing settings, documents and saved answers needs the owner's dashboard login.
Get AI reply settings and quota
/v1/ai-replyAI auto-reply settings and this month's quota. AI answers a 1:1 message only when no auto-reply rule or chatbot flow did, and steps back in a chat for pause_hours after a person from the business sends a message from the dashboard.
Responses
- 200State
Response fields
settingsobjectrequiredsettings.enabledbooleanrequiredsettings.instructionsstringrequiredPersona, tone, dos and don'tssettings.knowledgestringrequiredFacts the assistant may use: products, prices, hours, policies, FAQsettings.handoff_keywordsarray of stringrequiredA message containing one of these words pauses AI for the chat (lower-case, whole words)settings.handoff_messagestringrequiredSent when a customer asks for a person (empty = say nothing)settings.pause_hoursintegerrequiredHow long AI stays quiet in a chat after a person from the business replied or a handoffsettings.max_per_chat_per_dayintegerrequiredquotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MMavailablebooleanrequiredWhether the platform has an AI model set up
curl "https://api.chatkomodo.com/v1/ai-reply" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.chatkomodo.com/v1/ai-reply",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/ai-reply', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"settings": {
"enabled": true,
"instructions": "string",
"knowledge": "string",
"handoff_keywords": [
"string"
],
"handoff_message": "Hi Sam, your order has shipped.",
"pause_hours": 1,
"max_per_chat_per_day": 1
},
"quota": {
"limit": 50,
"used": 12,
"period": "string"
},
"available": true
}Update AI reply settings
/v1/ai-replyDashboard loginChange AI auto-reply settings (any subset). Owner login only. Turning it on needs some knowledge, instructions or a ready knowledge document. Audited as ai_reply.update.
Request body
enabledbooleaninstructionsstringPersona, tone, dos and don'tsknowledgestringFacts the assistant may use: products, prices, hours, policies, FAQhandoff_keywordsarray of stringA message containing one of these words pauses AI for the chat (lower-case, whole words)handoff_messagestringSent when a customer asks for a person (empty = say nothing)pause_hoursintegerHow long AI stays quiet in a chat after a person from the business replied or a handoffmax_per_chat_per_dayinteger
Responses
- 200Saved
Response fields
settingsobjectrequiredsettings.enabledbooleanrequiredsettings.instructionsstringrequiredPersona, tone, dos and don'tssettings.knowledgestringrequiredFacts the assistant may use: products, prices, hours, policies, FAQsettings.handoff_keywordsarray of stringrequiredA message containing one of these words pauses AI for the chat (lower-case, whole words)settings.handoff_messagestringrequiredSent when a customer asks for a person (empty = say nothing)settings.pause_hoursintegerrequiredHow long AI stays quiet in a chat after a person from the business replied or a handoffsettings.max_per_chat_per_dayintegerrequiredquotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MMavailablebooleanrequiredWhether the platform has an AI model set up
- 400
VALIDATION_FAILED - 403Not the owner
curl -X PUT "https://api.chatkomodo.com/v1/ai-reply" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"knowledge": "Acme Dental, Jl. Sudirman 12, Jakarta. Open Mon–Sat 9:00–17:00. Check-up Rp150.000. We accept BPJS and QRIS.",
"instructions": "Friendly and short. Call customers \"Kak\". Never promise discounts.",
"handoff_keywords": [
"admin",
"human",
"complaint"
]
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"enabled": true,
"knowledge": "Acme Dental, Jl. Sudirman 12, Jakarta. Open Mon–Sat 9:00–17:00. Check-up Rp150.000. We accept BPJS and QRIS.",
"instructions": "Friendly and short. Call customers \"Kak\". Never promise discounts.",
"handoff_keywords": [
"admin",
"human",
"complaint"
]
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.put(
"https://api.chatkomodo.com/v1/ai-reply",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"enabled": True,
"knowledge": "Acme Dental, Jl. Sudirman 12, Jakarta. Open Mon–Sat 9:00–17:00. Check-up Rp150.000. We accept BPJS and QRIS.",
"instructions": "Friendly and short. Call customers \"Kak\". Never promise discounts.",
"handoff_keywords": [
"admin",
"human",
"complaint",
],
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/ai-reply', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'enabled' => true,
'knowledge' => 'Acme Dental, Jl. Sudirman 12, Jakarta. Open Mon–Sat 9:00–17:00. Check-up Rp150.000. We accept BPJS and QRIS.',
'instructions' => 'Friendly and short. Call customers "Kak". Never promise discounts.',
'handoff_keywords' => [
'admin',
'human',
'complaint',
],
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"settings": {
"enabled": true,
"instructions": "string",
"knowledge": "string",
"handoff_keywords": [
"string"
],
"handoff_message": "Hi Sam, your order has shipped.",
"pause_hours": 1,
"max_per_chat_per_day": 1
},
"quota": {
"limit": 50,
"used": 12,
"period": "string"
},
"available": true
}Try a question on the assistant
/v1/ai-reply/previewDashboard loginAsk the assistant one question with the saved settings, or with draft settings before saving. Counts as one AI reply. Owner only, 10 per minute.
Request body
messagestringrequiredsettingsobjectsettings.enabledbooleansettings.instructionsstringPersona, tone, dos and don'tssettings.knowledgestringFacts the assistant may use: products, prices, hours, policies, FAQsettings.handoff_keywordsarray of stringA message containing one of these words pauses AI for the chat (lower-case, whole words)settings.handoff_messagestringSent when a customer asks for a person (empty = say nothing)settings.pause_hoursintegerHow long AI stays quiet in a chat after a person from the business replied or a handoffsettings.max_per_chat_per_dayinteger
Responses
- 200Answer
Response fields
replystringrequiredmsintegerrequiredsourcesarray of stringrequiredNames of the knowledge documents whose passages were given to the model;:faqstands for the business's saved answersunsurebooleanrequiredThe model said the information does not answer the question (a customer would get this reply and the question would be listed as unanswered)quotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MM
- 402No AI replies left this month (
LIMIT_REACHED) - 429More than 10 previews a minute
- 502The model did not answer (
UPSTREAM_ERROR) - 503The platform has no AI model set up (
NOT_CONFIGURED)
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/preview" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What time do you open on Saturday?"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/preview', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"message": "What time do you open on Saturday?"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/preview",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"message": "What time do you open on Saturday?",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/preview', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'message' => 'What time do you open on Saturday?',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"reply": "Hi Sam, your order has shipped.",
"ms": 1,
"sources": [
"string"
],
"unsure": true,
"quota": {
"limit": 50,
"used": 12,
"period": "string"
}
}Pause AI replies in a chat
/v1/ai-reply/chats/pauseStop AI replies in one chat for hours (1–720; default the tenant's pause_hours). Allowed for support agents.
Request body
session_idstringrequiredchat_idstringrequiredhoursinteger
Responses
- 200Done
Response fields
okbooleanrequired
- 400Unknown session or missing chat_id
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/chats/pause" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"hours": 12
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/chats/pause', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"hours": 12
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/chats/pause",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"hours": 12,
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/chats/pause', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'session_id' => 'YOUR_DEVICE_ID',
'chat_id' => '[email protected]',
'hours' => 12,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}Resume AI replies in a chat
/v1/ai-reply/chats/resumeLet AI reply in this chat again.
Request body
session_idstringrequiredchat_idstringrequired
Responses
- 200Done
Response fields
okbooleanrequired
- 400Unknown session or missing chat_id
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/chats/resume" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/chats/resume', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/chats/resume",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/chats/resume', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'session_id' => 'YOUR_DEVICE_ID',
'chat_id' => '[email protected]',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}List knowledge documents
/v1/ai-reply/documentsKnowledge documents for AI replies: files the business uploaded (price lists, menus, policies, FAQs), their reading status, and the plan's limits. When a customer writes, the passages that best match the message are given to the AI with the typed knowledge — the AI answers from them and says a person will follow up when they do not cover the question.
Responses
- 200State
Response fields
availablebooleanrequiredWhether the platform offers knowledge documents (switched off by the operator = false)limitsobjectrequiredlimits.filesintegerrequiredDocuments allowed by the plan (0 = not included)limits.mbintegerrequiredlimits.file_mbnumberrequiredLargest single filelimits.typesarray of stringrequiredusedobjectrequiredused.filesintegerrequiredused.mbnumberrequireddocumentsarray of objectrequireddocuments[].idstringrequireddocuments[].namestringrequiredFile name as uploadeddocuments[].extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocuments[].sizeintegerrequiredBytesdocuments[].statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocuments[].progressnumberrequired0–1 while parsingdocuments[].chunksintegerrequiredPassages the file was cut intodocuments[].errorstring | nullrequireddocuments[].enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)documents[].source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocuments[].created_atintegerrequireddocuments[].updated_atintegerrequired
curl "https://api.chatkomodo.com/v1/ai-reply/documents" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.chatkomodo.com/v1/ai-reply/documents",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/ai-reply/documents', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"available": true,
"limits": {
"files": 12,
"mb": 1,
"file_mb": 0.5,
"types": [
"string"
]
},
"used": {
"files": 12,
"mb": 0.5
},
"documents": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
]
}Upload a knowledge document
/v1/ai-reply/documentsDashboard loginUpload one file (multipart field file): PDF with selectable text, Word (.docx), Excel (.xlsx), CSV, text, Markdown or HTML, up to 8 MB. The knowledge engine reads it in the background (status parsing → ready); when the engine is not set up, unreachable or fails the file, the backup reader on the API server reads it instead (engine: local, usually ready at once). Owner login only; 30 uploads (files and links) per 10 minutes. Audited as ai_kb.upload.
Request body (multipart/form-data)
filestringrequiredThe file (binary)
Responses
- 201Accepted
Response fields
availablebooleanrequiredWhether the platform offers knowledge documents (switched off by the operator = false)limitsobjectrequiredlimits.filesintegerrequiredDocuments allowed by the plan (0 = not included)limits.mbintegerrequiredlimits.file_mbnumberrequiredLargest single filelimits.typesarray of stringrequiredusedobjectrequiredused.filesintegerrequiredused.mbnumberrequireddocumentsarray of objectrequireddocuments[].idstringrequireddocuments[].namestringrequiredFile name as uploadeddocuments[].extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocuments[].sizeintegerrequiredBytesdocuments[].statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocuments[].progressnumberrequired0–1 while parsingdocuments[].chunksintegerrequiredPassages the file was cut intodocuments[].errorstring | nullrequireddocuments[].enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)documents[].source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocuments[].created_atintegerrequireddocuments[].updated_atintegerrequireddocumentobjectrequireddocument.idstringrequireddocument.namestringrequiredFile name as uploadeddocument.extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocument.sizeintegerrequiredBytesdocument.statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocument.progressnumberrequired0–1 while parsingdocument.chunksintegerrequiredPassages the file was cut intodocument.errorstring | nullrequireddocument.enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)document.source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocument.created_atintegerrequireddocument.updated_atintegerrequired
- 400
VALIDATION_FAILED,EMPTY_FILE,BAD_FILE,BAD_ENCODING - 402
NOT_IN_PLANorLIMIT_REACHED(files or MB of the plan) - 403Not the owner
- 413
FILE_TOO_LARGE - 415
UNSUPPORTED_TYPE - 429Too many uploads
- 502
ENGINE_ERROR— the knowledge engine did not accept the request; try again - 503
NOT_CONFIGUREDorPLATFORM_FULL
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/documents" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-F "[email protected]"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('file', new Blob([await readFile('price-list.pdf')]), 'price-list.pdf');
const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
body: form,
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/documents",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
files={"file": open("price-list.pdf", "rb")},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/documents', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'multipart' => [
['name' => 'file', 'contents' => fopen('price-list.pdf', 'r')],
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"available": true,
"limits": {
"files": 12,
"mb": 1,
"file_mb": 0.5,
"types": [
"string"
]
},
"used": {
"files": 12,
"mb": 0.5
},
"documents": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
],
"document": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}Delete a knowledge document
/v1/ai-reply/documents/{id}Dashboard loginDelete a document: AI replies stop using it at once. Owner login only. Audited as ai_kb.delete.
Path parameters
idstringrequired
Responses
- 200Deleted
Response fields
availablebooleanrequiredWhether the platform offers knowledge documents (switched off by the operator = false)limitsobjectrequiredlimits.filesintegerrequiredDocuments allowed by the plan (0 = not included)limits.mbintegerrequiredlimits.file_mbnumberrequiredLargest single filelimits.typesarray of stringrequiredusedobjectrequiredused.filesintegerrequiredused.mbnumberrequireddocumentsarray of objectrequireddocuments[].idstringrequireddocuments[].namestringrequiredFile name as uploadeddocuments[].extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocuments[].sizeintegerrequiredBytesdocuments[].statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocuments[].progressnumberrequired0–1 while parsingdocuments[].chunksintegerrequiredPassages the file was cut intodocuments[].errorstring | nullrequireddocuments[].enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)documents[].source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocuments[].created_atintegerrequireddocuments[].updated_atintegerrequiredokbooleanrequired
- 403Not the owner
- 404Not found
curl -X DELETE "https://api.chatkomodo.com/v1/ai-reply/documents/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents/ID', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.delete(
"https://api.chatkomodo.com/v1/ai-reply/documents/ID",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('DELETE', 'https://api.chatkomodo.com/v1/ai-reply/documents/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"available": true,
"limits": {
"files": 12,
"mb": 1,
"file_mb": 0.5,
"types": [
"string"
]
},
"used": {
"files": 12,
"mb": 0.5
},
"documents": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
],
"ok": true
}Read a failed document again
/v1/ai-reply/documents/{id}/retryDashboard loginRead a failed document again. Owner login only.
Path parameters
idstringrequired
Responses
- 200Reading again
Response fields
documentobjectrequireddocument.idstringrequireddocument.namestringrequiredFile name as uploadeddocument.extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocument.sizeintegerrequiredBytesdocument.statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocument.progressnumberrequired0–1 while parsingdocument.chunksintegerrequiredPassages the file was cut intodocument.errorstring | nullrequireddocument.enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)document.source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocument.created_atintegerrequireddocument.updated_atintegerrequired
- 403Not the owner
- 404Not found
- 409
NOT_FAILED - 502
ENGINE_ERROR— the knowledge engine did not accept the request; try again - 503
NOT_CONFIGUREDorPLATFORM_FULL
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/documents/ID/retry" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents/ID/retry', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/documents/ID/retry",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/documents/ID/retry', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"document": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}Search the knowledge documents
/v1/ai-reply/documents/searchDashboard loginThe document passages an AI reply would get for question — to check that a document answers what customers ask. Does not call the AI and does not use the AI quota. Owner login only.
Request body
questionstringrequired
Responses
- 200Passages, best first
Response fields
resultsarray of objectrequiredresults[].documentstringrequiredDocument name, or:faqfor a saved answerresults[].textstringrequiredresults[].faqbooleanmsnumberrequired
- 400
VALIDATION_FAILED,EMPTY_FILE,BAD_FILE,BAD_ENCODING - 403Not the owner
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/documents/search" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"question": "How much is delivery to Bandung?"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents/search', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"question": "How much is delivery to Bandung?"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/documents/search",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"question": "How much is delivery to Bandung?",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/documents/search', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'question' => 'How much is delivery to Bandung?',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"results": [
{
"document": "string",
"text": "Hi Sam, your order has shipped.",
"faq": true
}
],
"ms": 0.5
}Add a web page as a document
/v1/ai-reply/documents/urlDashboard loginAdd a public web page (FAQ, shipping, prices) as a document: its text is read once (HTML or plain text, up to 3 MB, up to 4 redirects; private and local addresses are refused) and kept with Source: and Title: lines; name is the page title and source_url the final address. Pages that need JavaScript to show their text cannot be read (NO_TEXT). Counts as one file of the plan. Owner login only; shares the upload limit. Audited as ai_kb.upload (detail link <url>).
Request body
urlstring (uri)required
Responses
- 201Added
Response fields
availablebooleanrequiredWhether the platform offers knowledge documents (switched off by the operator = false)limitsobjectrequiredlimits.filesintegerrequiredDocuments allowed by the plan (0 = not included)limits.mbintegerrequiredlimits.file_mbnumberrequiredLargest single filelimits.typesarray of stringrequiredusedobjectrequiredused.filesintegerrequiredused.mbnumberrequireddocumentsarray of objectrequireddocuments[].idstringrequireddocuments[].namestringrequiredFile name as uploadeddocuments[].extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocuments[].sizeintegerrequiredBytesdocuments[].statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocuments[].progressnumberrequired0–1 while parsingdocuments[].chunksintegerrequiredPassages the file was cut intodocuments[].errorstring | nullrequireddocuments[].enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)documents[].source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocuments[].created_atintegerrequireddocuments[].updated_atintegerrequireddocumentobjectrequireddocument.idstringrequireddocument.namestringrequiredFile name as uploadeddocument.extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocument.sizeintegerrequiredBytesdocument.statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocument.progressnumberrequired0–1 while parsingdocument.chunksintegerrequiredPassages the file was cut intodocument.errorstring | nullrequireddocument.enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)document.source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocument.created_atintegerrequireddocument.updated_atintegerrequired
- 400
VALIDATION_FAILEDorURL_BLOCKED(private / local address) - 402
NOT_IN_PLANorLIMIT_REACHED(files or MB of the plan) - 403Not the owner
- 413
FILE_TOO_LARGE— page over 3 MB - 415
UNSUPPORTED_TYPE— not a web page or text (download the file and upload it) - 422
FETCH_FAILED(unreachable, HTTP error, too many redirects) orNO_TEXT - 429Too many uploads
- 503
NOT_CONFIGUREDorPLATFORM_FULL
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/documents/url" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/faq"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents/url', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"url": "https://example.com/faq"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/documents/url",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"url": "https://example.com/faq",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/documents/url', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'url' => 'https://example.com/faq',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"available": true,
"limits": {
"files": 12,
"mb": 1,
"file_mb": 0.5,
"types": [
"string"
]
},
"used": {
"files": 12,
"mb": 0.5
},
"documents": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
],
"document": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}Read a web page again
/v1/ai-reply/documents/{id}/refreshDashboard loginRead a web document's page again (after the page changed). The new copy gets a new id and replaces the old one once it is in; the plan limit counts them as one. Owner login only. Audited as ai_kb.upload (detail refresh <url>).
Path parameters
idstringrequired
Responses
- 201Read again
Response fields
availablebooleanrequiredWhether the platform offers knowledge documents (switched off by the operator = false)limitsobjectrequiredlimits.filesintegerrequiredDocuments allowed by the plan (0 = not included)limits.mbintegerrequiredlimits.file_mbnumberrequiredLargest single filelimits.typesarray of stringrequiredusedobjectrequiredused.filesintegerrequiredused.mbnumberrequireddocumentsarray of objectrequireddocuments[].idstringrequireddocuments[].namestringrequiredFile name as uploadeddocuments[].extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocuments[].sizeintegerrequiredBytesdocuments[].statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocuments[].progressnumberrequired0–1 while parsingdocuments[].chunksintegerrequiredPassages the file was cut intodocuments[].errorstring | nullrequireddocuments[].enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)documents[].source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocuments[].created_atintegerrequireddocuments[].updated_atintegerrequireddocumentobjectrequireddocument.idstringrequireddocument.namestringrequiredFile name as uploadeddocument.extstringrequiredpdf,docx,xlsx,csv,txt,mdorhtmldocument.sizeintegerrequiredBytesdocument.statusstringrequiredparsingwhile the engine reads the file (seconds to a few minutes),readywhen AI replies use it,failedwitherror(e.g. a scanned PDF without text),deletingwhile the engine copy is removeddocument.progressnumberrequired0–1 while parsingdocument.chunksintegerrequiredPassages the file was cut intodocument.errorstring | nullrequireddocument.enginestringrequiredWho read it:ragflow(the knowledge engine) orlocal(the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)document.source_urlstring | nullrequiredThe web page a document was read from (POST /v1/ai-reply/documents/url);nameis then the page titledocument.created_atintegerrequireddocument.updated_atintegerrequired
- 400
URL_BLOCKED - 403Not the owner
- 404Not found
- 409
NOT_A_PAGE— the document was uploaded as a file - 422
FETCH_FAILEDorNO_TEXT
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/documents/ID/refresh" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/documents/ID/refresh', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/documents/ID/refresh",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/documents/ID/refresh', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"available": true,
"limits": {
"files": 12,
"mb": 1,
"file_mb": 0.5,
"types": [
"string"
]
},
"used": {
"files": 12,
"mb": 0.5
},
"documents": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
],
"document": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"ext": "string",
"size": 12,
"status": "uploading",
"progress": 0.5,
"chunks": 12,
"error": "string",
"engine": "ragflow",
"source_url": "https://example.com/file.jpg",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}List saved answers
/v1/ai-reply/faqSaved answers: question/answer pairs written by the business. AI replies find them like document passages and prefer them (the business wrote them for exactly that question). Readable by agents.
Responses
- 200Newest first
Response fields
itemsarray of objectrequireditems[].idstringrequireditems[].questionstringrequireditems[].answerstringrequireditems[].sourcestringrequiredunanswered: written from a question the assistant could not answeritems[].created_atintegerrequireditems[].updated_atintegerrequired
curl "https://api.chatkomodo.com/v1/ai-reply/faq" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/faq', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.chatkomodo.com/v1/ai-reply/faq",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/ai-reply/faq', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"items": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"question": "Do you deliver on Sunday?",
"answer": "Hi Sam, your order has shipped.",
"source": "manual",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
]
}Add a saved answer
/v1/ai-reply/faqDashboard loginAdd a saved answer (at most 500 per business). Used by the next AI reply at once. Owner login only. Audited as ai_kb.faq (detail add).
Request body
questionstringrequiredanswerstringrequired
Responses
- 201Saved
Response fields
faqobjectrequiredfaq.idstringrequiredfaq.questionstringrequiredfaq.answerstringrequiredfaq.sourcestringrequiredunanswered: written from a question the assistant could not answerfaq.created_atintegerrequiredfaq.updated_atintegerrequired
- 400
VALIDATION_FAILED - 402
LIMIT_REACHED - 403Not the owner
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/faq" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"question": "Do you deliver on Sunday?",
"answer": "Yes, Sunday deliveries leave at 10:00 and arrive the same day in Jakarta."
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/faq', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"question": "Do you deliver on Sunday?",
"answer": "Yes, Sunday deliveries leave at 10:00 and arrive the same day in Jakarta."
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/faq",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"question": "Do you deliver on Sunday?",
"answer": "Yes, Sunday deliveries leave at 10:00 and arrive the same day in Jakarta.",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/faq', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'question' => 'Do you deliver on Sunday?',
'answer' => 'Yes, Sunday deliveries leave at 10:00 and arrive the same day in Jakarta.',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"faq": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"question": "Do you deliver on Sunday?",
"answer": "Hi Sam, your order has shipped.",
"source": "manual",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}Change a saved answer
/v1/ai-reply/faq/{id}Dashboard loginChange a saved answer. Owner login only. Audited as ai_kb.faq (detail edit).
Path parameters
idstringrequired
Request body
questionstringrequiredanswerstringrequired
Responses
- 200Saved
Response fields
faqobjectrequiredfaq.idstringrequiredfaq.questionstringrequiredfaq.answerstringrequiredfaq.sourcestringrequiredunanswered: written from a question the assistant could not answerfaq.created_atintegerrequiredfaq.updated_atintegerrequired
- 400
VALIDATION_FAILED - 403Not the owner
- 404Not found
curl -X PUT "https://api.chatkomodo.com/v1/ai-reply/faq/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"question": "Do you deliver on Sunday?",
"answer": "Yes, from 10:00."
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/faq/ID', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"question": "Do you deliver on Sunday?",
"answer": "Yes, from 10:00."
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.put(
"https://api.chatkomodo.com/v1/ai-reply/faq/ID",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"question": "Do you deliver on Sunday?",
"answer": "Yes, from 10:00.",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/ai-reply/faq/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'question' => 'Do you deliver on Sunday?',
'answer' => 'Yes, from 10:00.',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"faq": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"question": "Do you deliver on Sunday?",
"answer": "Hi Sam, your order has shipped.",
"source": "manual",
"created_at": 1767225600000,
"updated_at": 1767225600000
}
}Delete a saved answer
/v1/ai-reply/faq/{id}Dashboard loginDelete a saved answer. Owner login only. Audited as ai_kb.faq (detail delete).
Path parameters
idstringrequired
Responses
- 200Deleted
Response fields
okbooleanrequired
- 403Not the owner
- 404Not found
curl -X DELETE "https://api.chatkomodo.com/v1/ai-reply/faq/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/faq/ID', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.delete(
"https://api.chatkomodo.com/v1/ai-reply/faq/ID",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('DELETE', 'https://api.chatkomodo.com/v1/ai-reply/faq/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}List unanswered questions
/v1/ai-reply/unansweredQuestions the AI assistant could not answer from the business information (it told the customer the team will follow up). Open ones first, most asked first; status=all also lists answered and dismissed ones. At most 300 open per business (the least asked make room). Readable by agents.
Query parameters
statusstring
Responses
- 200List
Response fields
itemsarray of objectrequireditems[].idstringrequireditems[].questionstringrequiredThe customer's message (first wording seen)items[].countintegerrequiredHow many times it was asked (same words, ignoring case and punctuation)items[].session_idstring | nullrequireditems[].chat_idstring | nullrequiredThe latest chat that askeditems[].replystring | nullrequiredWhat the assistant answereditems[].statusstringrequireditems[].faq_idstring | nullrequireditems[].created_atintegerrequireditems[].last_atintegerrequiredopenintegerrequired
curl "https://api.chatkomodo.com/v1/ai-reply/unanswered" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/unanswered', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.chatkomodo.com/v1/ai-reply/unanswered",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/ai-reply/unanswered', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"items": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"question": "Do you deliver on Sunday?",
"count": 12,
"session_id": "YOUR_DEVICE_ID",
"chat_id": "+447700900123",
"reply": "Hi Sam, your order has shipped.",
"status": "open",
"faq_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"created_at": 1767225600000,
"last_at": 1767225600000
}
],
"open": 1
}Answer an unanswered question
/v1/ai-reply/unanswered/{id}/answerDashboard loginAnswer a question once: it becomes a saved answer (optionally with a cleaner question) that the next AI reply uses, and leaves the open list. Owner login only. Audited as ai_kb.faq (detail answer).
Path parameters
idstringrequired
Request body
answerstringrequiredquestionstring
Responses
- 200Saved
Response fields
okbooleanrequiredfaqobjectrequiredfaq.idstringrequiredfaq.questionstringrequiredfaq.answerstringrequiredfaq.sourcestringrequiredunanswered: written from a question the assistant could not answerfaq.created_atintegerrequiredfaq.updated_atintegerrequiredopenintegerrequired
- 400
VALIDATION_FAILED - 402
LIMIT_REACHED(500 saved answers) - 403Not the owner
- 404Not found
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/answer" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"answer": "Yes, we take children from age 2."
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/answer', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"answer": "Yes, we take children from age 2."
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/answer",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"answer": "Yes, we take children from age 2.",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/answer', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'answer' => 'Yes, we take children from age 2.',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"faq": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"question": "Do you deliver on Sunday?",
"answer": "Hi Sam, your order has shipped.",
"source": "manual",
"created_at": 1767225600000,
"updated_at": 1767225600000
},
"open": 1
}Dismiss an unanswered question
/v1/ai-reply/unanswered/{id}/dismissDashboard loginClose an open question without answering it. Owner login only.
Path parameters
idstringrequired
Responses
- 200Closed
Response fields
okbooleanrequiredopenintegerrequired
- 403Not the owner
- 404Not found or not open
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/dismiss" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/dismiss', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/dismiss",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/unanswered/ID/dismiss', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"open": 1
}Get the AI report
/v1/ai-reply/reportWhat the AI assistant did over the last 7, 30 or 90 days (default 30), by day in the business's time zone: replies, replies where it was unsure, handoffs to a person, chats served, answered rate (replies / all), most asked questions, documents used most, open unanswered questions, and this month's quota. Kept 90 days.
Query parameters
daysstring
Responses
- 200Report
Response fields
daysintegerrequiredtotalsobjectrequiredtotals.repliesintegerrequiredtotals.unsureintegerrequiredtotals.handoffsintegerrequiredtotals.chatsintegerrequiredtotals.answered_ratenumber | nullrequireddailyarray of objectrequireddaily[].daystringrequiredYYYY-MM-DDdaily[].repliesintegerrequireddaily[].unsureintegerrequireddaily[].handoffsintegerrequiredtop_questionsarray of objectrequiredtop_questions[].questionstringrequiredtop_questions[].countintegerrequiredtop_questions[].unsureintegerrequiredtop_sourcesarray of objectrequiredtop_sources[].sourcestringrequiredDocument name or:faqtop_sources[].countintegerrequiredopen_unansweredintegerrequiredquotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MM
- 400
VALIDATION_FAILED
curl "https://api.chatkomodo.com/v1/ai-reply/report?days=30" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/report?days=30', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.chatkomodo.com/v1/ai-reply/report?days=30",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/ai-reply/report?days=30', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"days": 1,
"totals": {
"replies": 1,
"unsure": 1,
"handoffs": 1,
"chats": 1,
"answered_rate": 0.5
},
"daily": [
{
"day": "string",
"replies": 1,
"unsure": 1,
"handoffs": 1
}
],
"top_questions": [
{
"question": "Do you deliver on Sunday?",
"count": 12,
"unsure": 1
}
],
"top_sources": [
{
"source": "string",
"count": 12
}
],
"open_unanswered": 1,
"quota": {
"limit": 50,
"used": 12,
"period": "string"
}
}Suggest a reply for a chat
/v1/ai-reply/suggestA draft of the next reply in a chat for the person answering it, from the last week of the conversation, the business information and the documents. Nothing is sent. Counts as one AI reply. Owner, and agents for chats assigned to them or unassigned.
Request body
session_idstringrequiredchat_idstringrequiredlangstringSummary language (default en)
Responses
- 200Draft
Response fields
textstringrequiredmsintegerrequiredsourcesarray of stringrequiredunsurebooleanrequiredThe business information did not answer it: check before sendingquotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MM
- 400
VALIDATION_FAILED(session not yours, chat missing) - 402
LIMIT_REACHED— no AI replies left this month - 403An agent asking about a chat assigned to someone else
- 422
NOTHING_TO_DO— no recent text messages in the chat - 429More than 20 a minute per person
- 502
UPSTREAM_ERROR - 503
NOT_CONFIGURED— no AI model on the platform
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/suggest" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/suggest', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/suggest",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/suggest', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'session_id' => 'YOUR_DEVICE_ID',
'chat_id' => '[email protected]',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"text": "Hi Sam, your order has shipped.",
"ms": 1,
"sources": [
"string"
],
"unsure": true,
"quota": {
"limit": 50,
"used": 12,
"period": "string"
}
}Summarize a chat
/v1/ai-reply/summaryA 3–5 line summary of a conversation (last two weeks) for whoever takes it over: what the customer wants, what was promised, open questions, next step. Counts as one AI reply. Same access as suggest.
Request body
session_idstringrequiredchat_idstringrequiredlangstringSummary language (default en)
Responses
- 200Summary
Response fields
textstringrequiredmsintegerrequiredquotaobjectrequiredquota.limitintegerrequiredAI replies included in the plan per month (0 = not included)quota.usedintegerrequiredquota.periodstringrequiredYYYY-MM
- 400
VALIDATION_FAILED(session not yours, chat missing) - 402
LIMIT_REACHED— no AI replies left this month - 403An agent asking about a chat assigned to someone else
- 422
NOTHING_TO_DO— no recent text messages in the chat - 429More than 20 a minute per person
- 502
UPSTREAM_ERROR - 503
NOT_CONFIGURED— no AI model on the platform
curl -X POST "https://api.chatkomodo.com/v1/ai-reply/summary" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"lang": "en"
}'const res = await fetch('https://api.chatkomodo.com/v1/ai-reply/summary', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"lang": "en"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.chatkomodo.com/v1/ai-reply/summary",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"session_id": "YOUR_DEVICE_ID",
"chat_id": "[email protected]",
"lang": "en",
},
timeout=30,
)
res.raise_for_status()
print(res.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/ai-reply/summary', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'session_id' => 'YOUR_DEVICE_ID',
'chat_id' => '[email protected]',
'lang' => 'en',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"text": "Hi Sam, your order has shipped.",
"ms": 1,
"quota": {
"limit": 50,
"used": 12,
"period": "string"
}
}