1Тойм
API нь өгөгдлийг JSON‑оор хүлээн авч, JSON‑оор буцаана. Байгууллагыг түлхүүрээс нь тодорхойлох тул хаягт байгууллагын нэр орохгүй.
Суурь хаяг
https://aichat.mn/api/v1- Биетэй хүсэлтэд
Content-Type: application/jsonтолгой илгээнэ. - Огноо — ISO 8601, UTC:
2026-11-04T09:30:12.000Z. - Мөнгөн дүн — бүхэл төгрөг:
94000нь 94,000₮. - Жагсаалт хуудаслагдана:
?page=1&pageSize=20→dataмассив баtotal,page,pageSizeталбар. Зурвасуудbeforeкурсор,limit‑ээр.
2Нэвтрэлт ба эрх
Хүсэлт бүрт байгууллагынхаа API түлхүүрийг Authorization толгойд илгээнэ.
- 1
Түлхүүр үүсгэнэ
Самбарын «Тохиргоо → API ба webhook» хэсэгт «Түлхүүр үүсгэх» дарж, нэр өгөөд хэрэгтэй эрхүүдээ сонгоно.
- 2
Нэг удаа хуулна
ak_live_…түлхүүр зөвхөн үүсгэх үед нэг удаа харагдана — бид зөвхөн hash‑ийг нь хадгална. - 3
Толгойд нэмнэ
Хүсэлт бүрт
Authorization: Bearer ak_live_…толгой илгээнэ.
Эрх (scope)
Түлхүүр зөвхөн сонгосон эрхийн замуудад хандана. Эрх дутуу бол 403, SCOPE_MISSING код буцна.
| Эрх | Юуг зөвшөөрөх | Замууд |
|---|---|---|
contacts:read | Харилцагчдын жагсаалт, дэлгэрэнгүйг унших |
|
contacts:write | Харилцагчийн мэдээллийг засах |
|
conversations:read | Яриа, зурвасыг унших |
|
messages:send | Ярианд зурвас илгээх |
|
orders:read | Захиалга унших |
|
orders:write | Захиалга үүсгэх, төлөв өөрчлөх |
|
products:read | Каталог унших |
|
products:write | Бараа нэмэх, засах |
|
bookings:read | Цаг захиалга, сул цаг унших |
|
bookings:write | Цаг захиалга үүсгэх |
|
3Хязгаар ба алдаа
Түлхүүр бүр минутад 120 хүртэл хүсэлт илгээнэ. Хариу бүрийн X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset толгой хязгаар, үлдсэн хүсэлт, хязгаар шинэчлэгдэх хугацааг заана. Хэтэрвэл 429 ирэх бөгөөд Retry-After толгой хэдэн секунд хүлээхийг заана.
Алдааны хэлбэр
Алдаа бүр HTTP төлөв, машинд уншигдах code, монгол message‑тэй JSON буцаана:
{
"statusCode": 403,
"code": "SCOPE_MISSING",
"message": "Энэ түлхүүрт orders:write эрх алга."
}| Төлөв | Утга |
|---|---|
| 400 / 422 | Хүсэлтийн өгөгдөл буруу — талбар дутуу эсвэл хэлбэр буруу. |
| 401 | Түлхүүр байхгүй, буруу эсвэл цуцлагдсан.API_KEY_MISSINGAPI_KEY_INVALID |
| 403 | Эрх дутуу, багцад API багтаагүй эсвэл байгууллагын эрх түр хаагдсан.SCOPE_MISSINGPLAN_FEATURE_UNAVAILABLETENANT_SUSPENDED |
| 404 | Нөөц олдсонгүй — өөр байгууллагын өгөгдөл ч мөн адил харагдахгүй. |
| 409 | Төлөвийн зөрчил: үлдэгдэл хүрэлцэхгүй (OUT_OF_STOCK), 24 цагийн хугацаа дууссан (WINDOW_CLOSED) г.м. |
| 429 | Хурдны хязгаар хэтэрсэн — Retry-After секунд хүлээнэ.RATE_LIMITED |
| 5xx | Түр алдаа — хэсэг хүлээгээд дахин оролдоно. |
4Замууд
Бүх зам суурь хаягийн араас. :id — нөөцийн UUID.
| Арга | Зам | Эрх | Тайлбар |
|---|---|---|---|
| Харилцагч | |||
| GET | /contacts | contacts:read | Харилцагчид — `q`, `tag`, `channelId` шүүлт, хуудаслалт |
| GET | /contacts/:id | contacts:read | Харилцагчийн карт, ярианы түүх |
| PATCH | /contacts/:id | contacts:write | Нэр, утас, и‑мэйл, шошго, тэмдэглэл, бөөн илгээлтээс татгалзсан эсэх |
| Яриа | |||
| GET | /conversations | conversations:read | Яриа — `status`, `surface`, `channelId`, `q` шүүлт, хуудаслалт |
| GET | /conversations/:id | conversations:read | Нэг яриа — харилцагч, суваг, бичих боломж (`window`) |
| GET | /conversations/:id/messages | conversations:read | Зурвасууд — шинээс хуучин руу, `before` курсор, `limit` ≤ 100 |
| POST | /conversations/:id/messages | messages:send | Харилцагч руу зурвас илгээх — 24 цагийн дүрмээр; AI тухайн ярианд зогсоно |
| Захиалга | |||
| GET | /orders | orders:read | Захиалга — `status`, `paymentStatus`, `q`, `from`, `to` шүүлт, хуудаслалт |
| POST | /orders | orders:write | Захиалга үүсгэх — барааны `sku`/`productId`, тоо; үнийг каталогоос тооцно |
| GET | /orders/:id | orders:read | Нэг захиалга, сүүлийн төлбөрийн оролдлоготой |
| PATCH | /orders/:id | orders:write | Төлөв өөрчлөх, төлбөрийг гараар тэмдэглэх (`PAID`, `REFUNDED`) |
| Бараа | |||
| GET | /products | products:read | Бараа — `q` (латин галигтай), `category`, `active` шүүлт, хуудаслалт |
| GET | /products/:id | products:read | Нэг бараа |
| POST | /products | products:write | Бараа нэмэх — SKU байгууллага дотор давтагдашгүй |
| PATCH | /products/:id | products:write | Бараа засах — үнэ, үлдэгдэл, идэвхтэй эсэх |
| Цаг захиалга | |||
| GET | /bookings | bookings:read | Цаг захиалга — `from`, `to`, `status`, `serviceId` шүүлт, хуудаслалт |
| GET | /bookings/:id | bookings:read | Нэг цаг захиалга |
| POST | /bookings | bookings:write | Цаг захиалах — дүүрсэн бол `409`; урьдчилгаатай бол төлбөр хүлээнэ |
| GET | /booking-services | bookings:read | Идэвхтэй үйлчилгээ — хугацаа, үнэ, урьдчилгаа, ажлын цаг |
| GET | /booking-services/:id/slots?date=YYYY-MM-DD | bookings:read | Үйлчилгээний тухайн өдрийн сул цаг (байгууллагын цагийн бүсээр) |
5Жишээ
Жишээн дэх ak_live_…‑г өөрийн түлхүүрээр солино.
Захиалгын жагсаалт авах
Шинэ захиалга эхэнд. Дараагийн хуудсыг page‑ээр авна.
curl https://aichat.mn/api/v1/orders?page=1&pageSize=20 \
-H "Authorization: Bearer ak_live_…"Захиалга үүсгэх
Барааг sku эсвэл productId‑аар өгнө — үнэ, хүргэлт, нийт дүнг сервер каталогоос тооцно. Хариунд дугаар, нийт дүн бүхий захиалга ирнэ.
curl -X POST https://aichat.mn/api/v1/orders \
-H "Authorization: Bearer ak_live_…" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"sku": "BAG-001",
"quantity": 1
}
],
"customer": {
"name": "Болор",
"phone": "99112233",
"address": "БЗД, 26-р хороо, 12-р байр"
},
"paymentMethod": "QPAY"
}'Харилцагчид зурвас илгээх
Messenger, Instagram, WhatsApp‑д харилцагчийн сүүлийн зурвасаас хойш 24 цагийн дотор л илгээнэ — үгүй бол 409 WINDOW_CLOSED. Илгээсэн зурвас инбоксод ажилтны хариу шиг харагдаж, AI тухайн ярианд түр зогсоно.
curl -X POST https://aichat.mn/api/v1/conversations/0c7d5a2e-8f1b-4c3d-9e6a-2b4f8d1c7a90/messages \
-H "Authorization: Bearer ak_live_…" \
-H "Content-Type: application/json" \
-d '{
"text": "Сайн байна уу! Захиалга тань өнөөдөр хүргэлтэд гарна."
}'Node.js
Түлхүүрийг орчны хувьсагчид хадгалж, серверээсээ дуудна.
const response = await fetch('https://aichat.mn/api/v1/orders?page=1&pageSize=20', {
headers: { Authorization: `Bearer ${process.env.AICHAT_API_KEY}` }
})
if (!response.ok) throw new Error(`aichat.mn API ${response.status}`)
const { data, total } = await response.json()6Webhook
Үйл явдал болмогц бид таны HTTPS хаяг руу JSON POST илгээнэ.
- 1
Хаяг нэмнэ
«Тохиргоо → API ба webhook» хэсэгт «Webhook нэмэх» дарж HTTPS хаягаа оруулаад хүлээн авах үйл явдлуудаа сонгоно (10 хүртэл хаяг).
- 2
Нууцаа хадгална
Гарын үсгийн нууц (secret) зөвхөн нэг удаа харагдана. Шаардлагатай үед «Нууц шинэчлэх»‑ээр сольж болно.
- 3
Туршина
«Туршилт илгээх» дарахад
pingүйл явдал очно. Хүргэлт бүрийн хариу, хугацааг түүхээс харна.
Үйл явдлууд
| Үйл явдал | Хэзээ илгээгдэх |
|---|---|
order.created | Чат, ажилтан эсвэл API‑аар шинэ захиалга үүсэхэд |
order.updated | Захиалгын төлөв, мэдээлэл өөрчлөгдөхөд |
order.paid | Захиалгын төлбөр орох эсвэл гараар баталгаажихад |
booking.created | Шинэ цаг захиалга үүсэхэд |
booking.updated | Цаг захиалгын төлөв, цаг өөрчлөгдөхөд |
contact.created | Шинэ харилцагч анх бичихэд |
conversation.handoff | Яриа ажилтанд шилжихэд |
message.received | Харилцагчаас шинэ зурвас ирэхэд |
Толгойнууд
| Толгой | Утга |
|---|---|
X-Aichat-Signature | t=<unix секунд>,v1=<hex> — HMAC‑SHA256 гарын үсэг |
X-Aichat-Event | Үйл явдлын нэр — order.paid г.м. |
X-Aichat-Delivery | Хүргэлтийн давтагдашгүй id — дахин оролдлогод ижил хэвээр |
Биеийн жишээ — order.paid
data нь тухайн нөөцийн API‑ийн хэлбэр — энд захиалга.
{
"id": "evt_5f0c2b9e1d473e8a",
"event": "order.paid",
"createdAt": "2026-11-04T09:34:52.000Z",
"tenant": {
"id": "6d2e8f4a-0b1c-4d3e-9f5a-7b8c9d0e1f2a",
"slug": "nomin-stor"
},
"data": {
"id": "9b2f6c1e-4d3a-4c55-9a43-1f2e3d4c5b6a",
"number": 1024,
"status": "CONFIRMED",
"paymentStatus": "PAID",
"paymentMethod": "QPAY",
"items": [
{
"id": "3e8a1f20-7c41-4b9e-8d5a-6f0c2b9e1d47",
"productId": "a1c4e7b2-5d8f-4a3b-9c6e-0f2d4b7a9e13",
"sku": "BAG-001",
"name": "Арьсан цүнх",
"unitPrice": 89000,
"quantity": 1,
"lineTotal": 89000
}
],
"subtotal": 89000,
"deliveryFee": 5000,
"total": 94000,
"customer": {
"name": "Болор",
"phone": "99112233",
"address": "БЗД, 26-р хороо, 12-р байр"
},
"note": "",
"source": "AI",
"attribution": "AI",
"attributedUser": null,
"contact": {
"id": "f4b8c2d6-1a3e-4f5b-8c7d-9e0a1b2c3d4e",
"externalId": "7712093384521",
"name": "Болор",
"username": null,
"avatarUrl": null
},
"conversationId": "0c7d5a2e-8f1b-4c3d-9e6a-2b4f8d1c7a90",
"channel": {
"id": "c2a7e9f1-3b5d-4c8e-a1f6-7d9b0e2c4a68",
"type": "INSTAGRAM",
"name": "nominstor",
"username": "nominstor",
"pictureUrl": null,
"status": "ACTIVE"
},
"payment": {
"id": "e7d1c3b5-9a2f-4e6d-8b0c-1f3a5e7c9b2d",
"method": "QPAY",
"amount": 94000,
"status": "PAID",
"shortUrl": "https://s.qpay.mn/…",
"qrImage": null,
"bankLinks": [],
"reference": null,
"expiresAt": "2026-11-05T09:30:12.000Z",
"paidAt": "2026-11-04T09:34:51.000Z",
"createdAt": "2026-11-04T09:30:12.000Z"
},
"createdBy": null,
"createdAt": "2026-11-04T09:30:12.000Z",
"updatedAt": "2026-11-04T09:34:51.000Z",
"paidAt": "2026-11-04T09:34:51.000Z",
"fulfilledAt": null,
"cancelledAt": null
}
}7Гарын үсэг шалгах
Хүсэлт биднээс ирсэн, замдаа өөрчлөгдөөгүйг гарын үсгээр шалгана:
X-Aichat-Signatureтолгойг,‑ээр хуваажt(unix секунд),v1(hex) утгыг авна.- Биеийг задлахаас өмнө түүхий байтаар нь авч,
t+.+ бие мөр үүсгэнэ. - Тэр мөрийн HMAC‑SHA256‑г webhook‑ийн нууцаар тооцож, hex болгоно.
- Тооцсон утгыг
v1‑тэй тогтмол хугацааны (timing‑safe) харьцуулалтаар тулгана. tодоогийн цагаас 300 секундээс их зөрвөл татгалзана — давтан илгээлтээс хамгаална.
Жишээ код
import crypto from 'node:crypto'
import express from 'express'
const SECRET = process.env.AICHAT_WEBHOOK_SECRET
const TOLERANCE = 300 // seconds
const verify = (header, rawBody) => {
const parts = Object.fromEntries(
String(header ?? '')
.split(',')
.map(part => part.trim().split('='))
)
const timestamp = Number(parts.t)
if (!parts.v1 || !Number.isInteger(timestamp)) return false
if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE) return false
const expected = crypto
.createHmac('sha256', SECRET)
.update(`${parts.t}.`)
.update(rawBody)
.digest()
const received = Buffer.from(parts.v1, 'hex')
return received.length === expected.length && crypto.timingSafeEqual(received, expected)
}
const app = express()
// Keep the raw body: the signature covers the exact bytes that were sent.
app.post('/webhooks/aichat', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.get('X-Aichat-Signature'), req.body)) return res.sendStatus(400)
const event = JSON.parse(req.body.toString('utf8'))
// Deduplicate by event.id, queue the work and answer quickly.
res.sendStatus(200)
})
app.listen(3000)import hashlib
import hmac
import os
import time
from flask import Flask, abort, request
SECRET = os.environ["AICHAT_WEBHOOK_SECRET"].encode()
TOLERANCE = 300 # seconds
def verify(header: str, raw_body: bytes) -> bool:
parts = dict(item.strip().split("=", 1) for item in header.split(",") if "=" in item)
try:
timestamp = int(parts["t"])
except (KeyError, ValueError):
return False
if abs(time.time() - timestamp) > TOLERANCE:
return False
signed = parts["t"].encode() + b"." + raw_body
expected = hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
app = Flask(__name__)
@app.post("/webhooks/aichat")
def aichat_webhook():
# request.get_data() is the raw body: the signature covers the exact bytes that were sent.
if not verify(request.headers.get("X-Aichat-Signature", ""), request.get_data()):
abort(400)
event = request.get_json()
# Deduplicate by event["id"], queue the work and answer quickly.
return "", 200<?php
$secret = getenv('AICHAT_WEBHOOK_SECRET');
$tolerance = 300; // seconds
// The raw body: the signature covers the exact bytes that were sent.
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_AICHAT_SIGNATURE'] ?? '';
$parts = [];
foreach (explode(',', $header) as $item) {
$pair = explode('=', trim($item), 2);
if (count($pair) === 2) {
$parts[$pair[0]] = $pair[1];
}
}
$timestamp = isset($parts['t']) ? (int) $parts['t'] : 0;
$expected = hash_hmac('sha256', ($parts['t'] ?? '') . '.' . $payload, $secret);
if (abs(time() - $timestamp) > $tolerance || !hash_equals($expected, $parts['v1'] ?? '')) {
http_response_code(400);
exit;
}
$event = json_decode($payload, true);
// Deduplicate by $event['id'], queue the work and answer quickly.
http_response_code(200);8Хүргэлт ба дахин оролдлого
- Хүлээн авмагц
2xxхариу буцааж, удаан ажлаа дараа нь хийнэ — 10 секундээс хэтрүүлэхгүй. 2xxбиш эсвэл хариугүй бол 6 хүртэл удаа дахин илгээнэ — 5 сек, 30 сек, 2 мин, 10 мин, 30 мин, 1 цаг завсартай (нийт 7 оролдлого).- Нэг үйл явдал хэд дахин ирж болно — биеийн
id‑гаар давхардлыг шалгана. - Ирэх дараалал баталгаагүй —
createdAt‑аар эрэмбэлнэ. - Дараалан 50 удаа амжилтгүй болсон хаягийг автоматаар унтраана — засаад самбараас дахин асаана.
- Хаяг заавал HTTPS, интернэтээс нээлттэй байна — дотоод сүлжээний IP руу илгээхгүй.
- Хүргэлтийн түүх 30 хоног хадгалагдана.
9Partner API (хүргэлт)
Хүргэлтийн компаниудад зориулсан тусдаа API. Байгууллагын ak_live_… биш, платформоос олгосон dp_live_… түлхүүрээр нэвтэрч, тухайн компанийг сонгосон бүх дэлгүүрийн хүргэлтийг авна.
- Суурь хаяг:
https://aichat.mn/api/partner/v1 - Нэвтрэлт:
Authorization: Bearer dp_live_…— түлхүүрийг платформ нэг л удаа өгнө - Хязгаар: partner бүрт минутад 300 хүсэлт
- Webhook:
delivery.created,delivery.cancelled— дээрх гарын үсгийн схемтэй ижил
| Арга | Зам | Тайлбар |
|---|---|---|
| GET | /me | Таны компанийн мэдээлэл — төлөв, холбогдсон дэлгүүрийн тоо, хурдны хязгаар |
| GET | /deliveries | Хүргэлтүүд — status, updatedSince (ISO огноо), page, pageSize шүүлт, хуудаслалт |
| GET | /deliveries/:id | Нэг хүргэлт — дэлгүүр, авах ба хүргэх хаяг, бараа, бэлнээр авах дүн, төлвийн түүх |
| POST | /deliveries/:id/accept | Хүргэлтийг хүлээн авах (PENDING → ACCEPTED) |
| POST | /deliveries/:id/reject | Татгалзах — reason заавал (PENDING → REJECTED) |
| POST | /deliveries/:id/status | Төлөв, курьер, tracking, хүргэх хугацаа, хүргэлтийн төлбөр шинэчлэх |
Төлвийн урсгал, webhook, жишээ код, partner болох заавар: Хүргэлтийн компаниудад