1Давуу тал
Дэлгүүр таныг хүргэлтийн компаниараа сонгосноос хойш захиалга бүр шууд танд ирнэ.
Захиалга өөрөө ирнэ
Захиалга бүр шууд танд хүргэлт болж очно — утсаар, чатаар дамжуулж бичих шаардлагагүй.
Зөв мэдээлэл
Хүлээн авагчийн нэр, утас, хаяг, бараа, бэлнээр авах дүн — захиалгаас яг илгээх мөчийн хуулбараар.
Нэг API — бүх дэлгүүр
Дэлгүүр бүртэй тусдаа холбогдохгүй: нэг түлхүүр, нэг webhook‑оор таныг сонгосон бүх дэлгүүр.
Харилцагч өөрөө мэднэ
Таны шинэчилсэн төлвөөр харилцагчид «Хүргэлтэд гарлаа», «Хүргэгдлээ» зурвас автоматаар очиж, дэлгүүрт мэдэгдэл ирнэ.
2Хэрхэн ажилладаг
Холболтоос эхний хүргэлт хүртэл дөрвөн алхам.
- 1
Хүсэлт илгээнэ
Компанийн нэр, хамрах бүс, үнийн нөхцөлөө hello@aichat.mn хаягаар илгээнэ. Бид таныг бүртгэж, API түлхүүр, webhook‑ийн нууцыг нэг удаа өгнө.
- 2
Дэлгүүрүүд таныг сонгоно
Дэлгүүр «Бараа ба захиалга → Тохиргоо» хэсгээс таныг хүргэлтийн компаниараа сонгоно — бүс, үнийн тайлбар тань харагдана.
- 3
Хүргэлт ирнэ
Дэлгүүр захиалгаас «Хүргэлтэд илгээх» дарах эсвэл төлбөр ормогц хүргэлт үүсч,
delivery.createdwebhook ирнэ. Webhook‑гүй болGET /deliveries‑ээр татна. - 4
Хүлээн авч, явцаа шинэчилнэ
Хүлээн авах эсвэл татгалзаад, курьер, tracking, төлвөө (авсан → замд → хүргэгдсэн) API‑аар илгээнэ.
3Төлвийн урсгал
Partner зөвхөн доорх шилжилтийг хийнэ — бусад нь 422 (DELIVERY_INVALID_TRANSITION) буцааж, хариунд зөвшөөрөгдөх төлвүүд (allowed) ирнэ. Эцсийн төлөвт хүрсэн хүргэлт дахин өөрчлөгдөхгүй.
- Хүлээгдэж буй
PENDINGЭсвэлТатгалзсанREJECTED - Хүлээн авсан
ACCEPTEDЭсвэлБүтэлгүйтсэнFAILED - Курьер авсан
PICKED_UPЭсвэлБүтэлгүйтсэнFAILEDшууд «Хүргэгдсэн» болгож болно
- Замд
IN_TRANSITЭсвэлБүтэлгүйтсэнFAILED - Хүргэгдсэн
DELIVERED
4API тойм
Хүсэлт бүрт Authorization: Bearer dp_live_… толгой илгээнэ. Түлхүүр зөвхөн таны компанид илгээсэн хүргэлтэд хандана.
Суурь хаяг
https://aichat.mn/api/partner/v1| Арга | Зам | Тайлбар |
|---|---|---|
| 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, хүргэх хугацаа, хүргэлтийн төлбөр шинэчлэх |
Алдаа
| Төлөв | Утга |
|---|---|
| 401 | Түлхүүр байхгүй (PARTNER_KEY_MISSING), буруу эсвэл шинэчлэгдсэн (PARTNER_KEY_INVALID). |
| 403 | Partner‑ийн бүртгэл түр хаагдсан (PARTNER_SUSPENDED). |
| 404 | Хүргэлт олдсонгүй (DELIVERY_NOT_FOUND) — өөр компанид илгээсэн хүргэлт ч адил харагдахгүй. |
| 409 | Хүргэлт тэр зуур өөрчлөгдсөн (DELIVERY_CHANGED) — дахин уншаад оролдоно. |
| 422 | Төлвийн шилжилт зөвшөөрөгдөөгүй (DELIVERY_INVALID_TRANSITION) — жишээ нь хүргэгдсэнийг буцааж «Замд» болгох; эсвэл талбар буруу. |
| 429 | Хурдны хязгаар хэтэрсэн (RATE_LIMITED) — Retry-After секунд хүлээнэ. Буруу түлхүүрээр олон дахин оролдвол тухайн IP түр хязгаарлагдана. |
Жишээ
Хүлээгдэж буй хүргэлтүүд
curl https://aichat.mn/api/partner/v1/deliveries?status=PENDING&pageSize=20 \
-H "Authorization: Bearer dp_live_…"Курьер авч гарсныг мэдэгдэх
curl -X POST https://aichat.mn/api/partner/v1/deliveries/5d1c9e2a-7b3f-4a8e-9c6d-2f1e0b7a4c38/status \
-H "Authorization: Bearer dp_live_…" \
-H "Content-Type: application/json" \
-d '{
"status": "PICKED_UP",
"courierName": "Ганбаатар",
"courierPhone": "88112233",
"trackingCode": "SH-1024",
"trackingUrl": "https://shuurkhai.mn/track/SH-1024",
"etaAt": "2026-11-04T12:00:00.000Z",
"feeMnt": 5000
}'Татгалзах
curl -X POST https://aichat.mn/api/partner/v1/deliveries/5d1c9e2a-7b3f-4a8e-9c6d-2f1e0b7a4c38/reject \
-H "Authorization: Bearer dp_live_…" \
-H "Content-Type: application/json" \
-d '{
"reason": "Хамрах бүсээс гадуур"
}'Node.js — webhook‑гүйгээр татах
const response = await fetch('https://aichat.mn/api/partner/v1/deliveries?status=PENDING&pageSize=50', {
headers: { Authorization: `Bearer ${process.env.AICHAT_PARTNER_KEY}` }
})
if (response.status === 429) {
// Rate limited: wait for Retry-After seconds, then try again.
}
const { data, total } = await response.json()Хүргэлтийн бие — GET /deliveries/:id
codAmount — хүргэхэд бэлнээр авах дүн (0 бол төлөгдсөн). Нийт дүн, барааг дэлгүүрийн захиалгаас авна — partner өөрчлөхгүй.
{
"id": "5d1c9e2a-7b3f-4a8e-9c6d-2f1e0b7a4c38",
"status": "PENDING",
"orderNumber": 1024,
"shop": {
"slug": "nomin-stor",
"name": "Номин Стор",
"phone": "77001234",
"address": "СБД, 1-р хороо, Номин төв"
},
"pickup": {
"name": "Номин Стор",
"phone": "77001234",
"address": "СБД, 1-р хороо, Номин төв"
},
"dropoff": {
"name": "Болор",
"phone": "99112233",
"address": "БЗД, 26-р хороо, 12-р байр"
},
"items": [
{
"name": "Арьсан цүнх",
"sku": "BAG-001",
"quantity": 1
}
],
"codAmount": 0,
"orderTotal": 94000,
"note": "Оройн 18:00-аас хойш",
"trackingCode": null,
"trackingUrl": null,
"courierName": null,
"courierPhone": null,
"etaAt": null,
"feeMnt": null,
"events": [
{
"status": "PENDING",
"at": "2026-11-04T09:34:52.000Z",
"note": null,
"by": "system"
}
],
"createdAt": "2026-11-04T09:34:52.000Z",
"updatedAt": "2026-11-04T09:34:52.000Z"
}5Webhook
Шинэ хүргэлт, цуцлалтыг таны HTTPS хаяг руу JSON POST‑оор шууд илгээнэ. Гарын үсэг нь хөгжүүлэгчийн webhook‑тэй ижил схемтэй.
| Үйл явдал | Хэзээ илгээгдэх |
|---|---|
delivery.created | Дэлгүүр захиалгыг танд илгээх, эсвэл төлбөр ормогц автоматаар |
delivery.cancelled | Дэлгүүр дуусаагүй хүргэлтийг цуцлахад |
Толгойнууд
| Толгой | Утга |
|---|---|
X-Aichat-Signature | t=<unix секунд>,v1=<hex> — HMAC‑SHA256 гарын үсэг |
X-Aichat-Event | Үйл явдлын нэр — delivery.created г.м. |
X-Aichat-Delivery | Хүргэлтийн давтагдашгүй id — дахин оролдлогод ижил хэвээр |
Биеийн жишээ — delivery.created
{
"id": "evt_7b3f4a8e9c6d2f1e0b7a4c38",
"event": "delivery.created",
"createdAt": "2026-11-04T09:34:53.000Z",
"data": {
"id": "5d1c9e2a-7b3f-4a8e-9c6d-2f1e0b7a4c38",
"status": "PENDING",
"orderNumber": 1024,
"shop": {
"slug": "nomin-stor",
"name": "Номин Стор",
"phone": "77001234",
"address": "СБД, 1-р хороо, Номин төв"
},
"pickup": {
"name": "Номин Стор",
"phone": "77001234",
"address": "СБД, 1-р хороо, Номин төв"
},
"dropoff": {
"name": "Болор",
"phone": "99112233",
"address": "БЗД, 26-р хороо, 12-р байр"
},
"items": [
{
"name": "Арьсан цүнх",
"sku": "BAG-001",
"quantity": 1
}
],
"codAmount": 0,
"orderTotal": 94000,
"note": "Оройн 18:00-аас хойш",
"trackingCode": null,
"trackingUrl": null,
"courierName": null,
"courierPhone": null,
"etaAt": null,
"feeMnt": null,
"events": [
{
"status": "PENDING",
"at": "2026-11-04T09:34:52.000Z",
"note": null,
"by": "system"
}
],
"createdAt": "2026-11-04T09:34:52.000Z",
"updatedAt": "2026-11-04T09:34:52.000Z"
}
}Гарын үсэг шалгах — Node.js
Биеийг задлахаас өмнө түүхий байтаар нь t + . + бие мөрийн HMAC‑SHA256‑г webhook‑ийн нууцаар тооцож, v1‑тэй тогтмол хугацааны харьцуулалтаар тулгана. t одоогийн цагаас 300 секундээс их зөрвөл татгалзана.
import crypto from 'node:crypto'
import express from 'express'
const SECRET = process.env.AICHAT_PARTNER_WEBHOOK_SECRET
const KEY = process.env.AICHAT_PARTNER_KEY
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-delivery', express.raw({ type: 'application/json' }), async (req, res) => {
if (!verify(req.get('X-Aichat-Signature'), req.body)) return res.sendStatus(400)
const event = JSON.parse(req.body.toString('utf8'))
res.sendStatus(200) // answer quickly, then do the work
if (event.event === 'delivery.created') {
// Deduplicate by event.id, then accept the delivery.
await fetch(`https://aichat.mn/api/partner/v1/deliveries/${event.data.id}/accept`, {
method: 'POST',
headers: { Authorization: `Bearer ${KEY}` }
})
}
})
app.listen(3000)6Partner болох
Хүргэлт, курьерийн үйлчилгээ эрхэлдэг бол бидэнд бичээрэй. Холболт үнэгүй.
Хүсэлтэд бичих зүйл
- Компанийн нэр, регистр, холбоо барих хүн
- Хамрах бүс, хүргэлтийн хугацаа, үнийн нөхцөл
- Webhook хүлээн авах HTTPS хаяг (заавал биш)
- Техникийн асуудлаар холбогдох и‑мэйл
API ба webhook · hello@aichat.mn