UMAI CRM · Dasturchilar uchun

Ochiq API v1

Tashqi tizimingiz (sayt, mobil ilova, ichki CRM) UMAI bilan server-to-server ishlashi uchun:

  • Bilim kartalarini sinxronlash: AI agent javob beradigan ma'lumotlar (FAQ, tariflar, qoidalar) sizning tizimingizdan avtomatik yangilanadi.
  • API kanal: saytingiz yoki ilovangizdagi support oynasidan mijoz xabari UMAI suhbatlariga tushadi, 24/7 AI agent yoki operator javob beradi, javob esa serveringizga imzolangan callback bilan qaytadi.

Base URL: https://app.umari.uz/public/v1. Versiya v1: buzuvchi o'zgarishlar faqat v2 da bo'ladi (yangi ixtiyoriy maydonlar qo'shilishi mumkin).

API kalit va kanal UMAI ichida yaratiladi: kirish, so'ng Integratsiyalar va Chat kanallari sahifalari.

1. Nega login tokeni emas, API kalit

UMAI login tokeni 2 soatlik va "bitta qurilma" qoidasi bilan ishlaydi: server undan foydalansa operator seanslari yopilib ketadi. Shuning uchun tashqi tizimlar uchun alohida, doimiy va cheklangan huquqli API kalit ishlatiladi. API faqat serverdan chaqiriladi: brauzer yoki mobil ilova ichiga kalit qo'yilmaydi (CORS ochilmaydi).

2. API kalit

  1. UMAI: Integratsiyalar → API kalitlar → Yangi kalit. Nom va huquqlarni (scope) tanlang.
  2. Kalit (umai_sk_..., 51 belgi) faqat bir marta ko'rsatiladi. Uni darhol secret manager'ga (yoki server .env ga) saqlang. UMAI bazasida faqat xeshi turadi: yo'qolsa tiklab bo'lmaydi, yangisini yarating.
  3. Har so'rovda sarlavha: Authorization: Bearer umai_sk_.... Kalitni URL'ga (query string) qo'ymang: u e'tiborga olinmaydi.
  4. Almashtirish (rotatsiya): yangi kalit yarating → tizimingizga deploy qiling → eski kalitni "Bekor qilish". Bekor qilish darhol kuchga kiradi.
  5. Kompaniyada bir vaqtda 10 tagacha faol kalit bo'ladi. Ro'yxatda kalit prefiksi (umai_sk_Ab3x) va oxirgi ishlatilgan vaqt ko'rinadi.
ScopeNima beradi
knowledgeBilim kartalarini sinxronlash (/knowledge/*)
chatAPI kanal xabarlari (/channels/*)

Funksiya tarifdagi "API va webhooklar" (api_webhooks) imkoniyati bilan ishlaydi. Tarifda bo'lmasa so'rovlar 403 qaytaradi. Tekshirish: GET /public/v1/me (scope talab qilinmaydi).

curl -s https://app.umari.uz/public/v1/me -H "Authorization: Bearer $UMAI_API_KEY"
# {"success":true,"data":{"key":{"prefix":"umai_sk_Ab3x","name":"Sayt support","scopes":["knowledge","chat"]}}}

3. Javob konverti, xatolar va limitlar

Muvaffaqiyat: {"success": true, "data": ...}. Xato: {"success": false, "error": "matn", "code": "KOD", ...qo'shimcha}. Barcha javoblarda Cache-Control: no-store.

HTTPcodeMa'nosi
400VALIDATIONMaydon noto'g'ri (field, sync'da index, externalId ham qaytadi)
400CONTEXT_INVALID, CONTEXT_TOO_LARGEMijoz konteksti noto'g'ri yoki juda katta
401API_KEY_REQUIREDAuthorization: Bearer umai_sk_... sarlavhasi yo'q yoki noto'g'ri formatda
401API_KEY_INVALID / API_KEY_REVOKEDKalit topilmadi / bekor qilingan
403COMPANY_<STATUS>Kompaniya faol emas (masalan COMPANY_SUSPENDED)
403PLAN_REQUIRED (reason: FEATURE_NOT_INCLUDED / SUBSCRIPTION_EXPIRED)Tarifda imkoniyat yo'q yoki obuna tugagan
403API_SCOPE (required)Kalitda shu huquq yo'q
404CHANNEL_NOT_FOUND, NOT_FOUNDKanal (yoki marshrut) topilmadi
409IDEMPOTENCY_CONFLICTShu messageId boshqa mazmun bilan yuborilgan
409IN_PROGRESSShu messageId hali bajarilmoqda (Retry-After: 5)
409CHAT_DISABLED, CHANNEL_DISABLEDChat yoki shu kanal UMAI sozlamalarida o'chiq
409KNOWLEDGE_LIMITKartalar limiti (API: 300, kompaniyada jami: 500)
429RATE_LIMITEDSo'rovlar juda ko'p
502CHAT_UNREACHABLEChat serveri javob bermadi: xuddi shu messageId bilan qayta yuboring

Limitlar: IP bo'yicha 600 so'rov/daqiqa, kalit bo'yicha 300 so'rov/daqiqa. Javobda RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset sarlavhalari bor. Tana hajmi 1 MB gacha.

4. Bilim kartalari (scope knowledge)

Karta maydonlari: externalId (sizning ID'ingiz: ^[A-Za-z0-9][A-Za-z0-9._:@-]{0,63}$), type (faq | script | product | policy), title (1–200), content (1–20000), active (ixtiyoriy, true/false; yuborilmasa o'zgarmaydi, yangi karta faol bo'ladi).

  • API orqali yozilgan kartalar UMAI'da "API" belgisi bilan ko'rinadi: admin ularning matnini o'zgartira olmaydi va o'chira olmaydi (faqat yoqish/o'chirish), mazmun sizning tizimingizdan boshqariladi.
  • Qo'lda yozilgan kartalarga API hech qachon tegmaydi: ro'yxat, sync va o'chirish faqat API kartalari bilan ishlaydi.
  • Limit: API kartalari 300 tagacha, kompaniyadagi jami kartalar 500 tagacha.
  • Matndan <bilim_kartalari>, <mijoz_konteksti> kabi xizmat teglari va boshqaruv belgilari olib tashlanadi.
  • AI keyingi javobdayoq yangi ma'lumotni ishlatadi (kesh yo'q).

Bitta karta (upsert):

curl -s -X PUT https://app.umari.uz/public/v1/knowledge/faq-delivery \
  -H "Authorization: Bearer $UMAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"type":"faq","title":"Yetkazib berish","content":"Toshkent bo'\''ylab 1 kunda, viloyatlarga 3 kunda."}'
# {"success":true,"data":{"externalId":"faq-delivery","result":"created","item":{...}}}
# result: created | updated | unchanged (mazmun bir xil bo'lsa yozilmaydi)

Ro'yxat va o'chirish:

curl -s https://app.umari.uz/public/v1/knowledge -H "Authorization: Bearer $UMAI_API_KEY"
# {"items":[{"externalId":"faq-delivery","type":"faq","title":"...","content":"...","active":true,"updatedAt":"..."}],"total":1}

curl -s -X DELETE https://app.umari.uz/public/v1/knowledge/faq-delivery -H "Authorization: Bearer $UMAI_API_KEY"
# {"externalId":"faq-delivery","deleted":true}   (yo'q bo'lsa deleted:false: takror chaqirish xavfsiz)

To'plam sinxroni (1–100 karta va tana 1 MB gacha, atomar: bitta xato bo'lsa hech biri yozilmaydi):

curl -s -X POST https://app.umari.uz/public/v1/knowledge/sync \
  -H "Authorization: Bearer $UMAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"items":[{"externalId":"plan-pro","type":"product","title":"Pro tarif","content":"Oyiga 300 000 so'\''m."}],"deleteMissing":false}'
# {"created":1,"updated":0,"unchanged":0,"deleted":0}

deleteMissing: true: ro'yxatda yo'q API kartalari o'chiriladi (to'liq ko'zgu). Bo'sh items rad etiladi (tasodifiy hammasini o'chirishdan himoya).

deleteMissing faqat barcha API kartalaringiz bitta so'rovga sig'sa ishlaydi (≤100 ta va ≤1 MB): har so'rov o'zida yo'q API kartalarini o'chiradi, shuning uchun bo'laklab yuborishda deleteMissing:true oldingi bo'laklarni o'chirib yuboradi. Kartalar ko'p bo'lsa deleteMissing:false bilan bo'laklab yuboring va keraksizlarini DELETE /knowledge/{externalId} bilan o'chiring.

5. API kanal (scope chat)

UMAI: Chat kanallari → API kanal → Ulash. Nom va (ixtiyoriy) callback URL kiriting. Natijada: channelId va imzo siri (whsec_..., faqat bir marta ko'rsatiladi). Callback URL bo'lmasa kanal "faqat pull" rejimida ishlaydi.

Oqim: mijoz sizning oynangizda yozadi → serveringiz POST /channels/{channelId}/messages → xabar UMAI suhbatlarida paydo bo'ladi → 24/7 AI agent (yoqilgan bo'lsa) yoki operator javob beradi → javob callback URL'ingizga keladi.

curl -s -X POST https://app.umari.uz/public/v1/channels/3/messages \
  -H "Authorization: Bearer $UMAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"messageId":"m-9f1c2","user":{"id":"u-5512","name":"Dilnoza"},"text":"Sinov muddatim qachon tugaydi?","context":{"plan":"Pro","trial_days_left":3}}'
# {"success":true,"data":{"status":"recorded","duplicate":false,"messageId":"m-9f1c2","conversationId":812,"umaiMessageId":99101}}
  • messageId majburiy idempotentlik kaliti (^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$). Qayta yuborishda aynan shu qiymatni ishlating: bir xil messageId + bir xil mazmun → 200 duplicate:true (xabar ikki marta yozilmaydi); boshqa mazmun → 409 IDEMPOTENCY_CONFLICT.
  • 502 CHAT_UNREACHABLE yoki 409 IN_PROGRESS bo'lsa biroz kutib (masalan 5 s, keyin oshirib) xuddi shu messageId bilan qayta yuboring. Idempotentlik oynasi 30 kun.
  • user.id: sizning foydalanuvchi ID'ingiz (xuddi shu format). Bitta (channelId, user.id) = UMAI'da bitta suhbat. user.name 80 belgigacha.
  • text 1–4000 belgi, v1 da faqat matn (attachments qabul qilinmaydi).
  • Mijoz telefoni so'ralmaydi va yuborilmaydi. API kanal suhbati UMAI'da lid (buyurtma kartasi) yaratmaydi.
  • Operator taqsimlash kompaniya sozlamasi bo'yicha: smenada onlayn operator bo'lsa suhbat unga biriktiriladi va AI jim turadi; operator bo'lmasa (masalan kechasi) 24/7 AI agent javob beradi.
  • Ma'lum cheklov: juda kam holatda (chat serveri javobi kechikib, aslida xabar yozilgan bo'lsa) qayta yuborish chat serverida ikkinchi nusxa yaratishi mumkin.

6. Mijoz konteksti (ixtiyoriy)

context: shu mijoz haqidagi ichki ma'lumot (tarif, sinov kunlari, til). Mijozga ko'rinmaydi, xabarlar tarixiga yozilmaydi, callback'larga chiqmaydi: faqat 24/7 AI agent uni ma'lumot sifatida ishlatadi ("Sinovingiz 3 kundan keyin tugaydi").

  • Tekis obyekt, ko'pi bilan 20 kalit, kalit ^[a-z][a-z0-9_]{0,39}$.
  • Qiymat: matn (200 belgigacha, ortig'i kesiladi), son yoki true/false. Ichma-ich obyekt va ro'yxat mumkin emas. JSON 2000 belgigacha.
  • Yuborilsa avvalgisini to'liq almashtiradi; "context": null tozalaydi; yuborilmasa o'zgarmaydi.
  • Parol, karta raqami, token yoki boshqa maxfiy ma'lumot yubormang.

7. Callback (UMAI → sizning serveringiz)

POST <callbackUrl>
Content-Type: application/json
User-Agent: UMAI-Webhook/1
X-Umai-Event: message.created
X-Umai-Event-Id: evt_1532          (barcha urinishlarda bir xil)
X-Umai-Timestamp: 1792569249       (shu urinishning unix soniyasi)
X-Umai-Attempt: 2
X-Umai-Signature: sha256=<hex HMAC_SHA256(signingSecret, X-Umai-Timestamp + "." + rawBody)>
{"id":"evt_1532","type":"message.created","createdAt":"2026-10-21T09:14:09.120Z","channelId":3,
 "conversation":{"id":812,"userId":"u-5512"},
 "message":{"id":99102,"text":"Sinovingiz 3 kundan keyin tugaydi.","sender":"bot","createdAt":"2026-10-21T09:14:08.950Z","hasAttachments":false}}

sender: bot (AI javobi, salomlashish, "operator tez orada javob beradi") yoki operator. Operator ismi, kompaniya ma'lumotlari va mijoz konteksti yuborilmaydi. Sinov tugmasi {"id":"evt_ping_<uuid>","type":"ping","createdAt":"...","channelId":3} yuboradi (xuddi shunday imzolanadi).

Qabul qiluvchi shartnomasi:

  1. Imzoni xom tana (rawBody, JSON parse qilishdan oldin) ustida tekshiring, solishtirish timingSafeEqual / hmac.compare_digest bilan.
  2. |hozir − X-Umai-Timestamp| ≤ 300 soniya bo'lsin (eski so'rovni qayta yuborishdan himoya).
  3. X-Umai-Event-Id bo'yicha dedupe qiling: yetkazish at-least-once (bir event bir necha marta kelishi mumkin).
  4. Tartib kafolatlanmaydi: xabarlarni message.createdAt bo'yicha saralang.
  5. 5 soniya ichida 2xx qaytaring (og'ir ishni navbatga qo'ying). Redirect kuzatilmaydi (3xx = muvaffaqiyatsiz). Javob tanasi o'qilmaydi.
  6. Productionda faqat https://; ichki yoki xususiy manzillar (localhost, 10.x, 192.168.x, 169.254.x, ...) bloklanadi.

Qayta urinish: 2xx bo'lmagan har qanday javob, timeout yoki tarmoq xatosida 10 s, 1 daq, 5 daq, 15 daq, 1 soat, 3 soat, 6 soat, 12 soat keyin (jami 9 urinish, ~22 soat). Undan keyin yetkazish "failed" bo'ladi: UMAI'da kanal kartasidagi "Qayta yuborish" tugmasi oxirgi 7 kundagi muvaffaqiyatsizlarini qayta navbatga qo'yadi (Event-Id o'zgarmaydi). Imzo siri almashtirilgandan keyingi urinishlar yangi sir bilan imzolanadi. Timeout yoki tarmoq xatosida shu kanalning navbatdagi boshqa javoblari 30 soniyaga kechiktiriladi.

Node.js (Express) namunasi:

const crypto = require("crypto");
const express = require("express");
const app = express();
const SECRET = process.env.UMAI_SIGNING_SECRET; // whsec_...
const seen = new Set(); // productionda: bazadagi unique jadval

app.post("/umai/callback", express.raw({ type: "application/json" }), (req, res) => {
    const ts = String(req.get("X-Umai-Timestamp") || "");
    const sig = String(req.get("X-Umai-Signature") || "");
    const raw = req.body.toString("utf8");
    const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(`${ts}.${raw}`).digest("hex");
    const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
    if (!ok || !/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
    const event = JSON.parse(raw);
    if (seen.has(event.id)) return res.sendStatus(200);
    seen.add(event.id);
    if (event.type === "message.created") {
        // event.conversation.userId foydalanuvchisiga event.message.text ni ko'rsating (navbat orqali)
    }
    res.sendStatus(200);
});

Python (Flask) namunasi:

import hashlib, hmac, os, time
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["UMAI_SIGNING_SECRET"].encode()

@app.post("/umai/callback")
def umai_callback():
    ts = request.headers.get("X-Umai-Timestamp", "")
    sig = request.headers.get("X-Umai-Signature", "")
    raw = request.get_data()  # xom tana (bytes)
    expected = "sha256=" + hmac.new(SECRET, ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300 or not hmac.compare_digest(sig, expected):
        abort(401)
    event = request.get_json()
    # event["id"] bo'yicha dedupe; event["message"]["text"] -> event["conversation"]["userId"]
    return "", 200

PHP namunasi:

$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_UMAI_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_UMAI_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, getenv('UMAI_SIGNING_SECRET'));
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300 || !hash_equals($expected, $sig)) { http_response_code(401); exit; }
$event = json_decode($raw, true);
http_response_code(200);

8. Pull (zaxira)

Callback'lar uzilib qolgan bo'lsa bo'shliqni to'ldirish yoki callback URL'siz ("faqat pull") kanal uchun:

curl -s "https://app.umari.uz/public/v1/channels/3/messages?userId=u-5512&after=99000&limit=50" -H "Authorization: Bearer $UMAI_API_KEY"
# {"conversationId":812,"items":[{"id":99101,"direction":"in","sender":"customer","text":"...","createdAt":"..."},
#                               {"id":99102,"direction":"out","sender":"bot","text":"...","createdAt":"..."}]}

after: oxirgi olingan xabar id si (kursor), limit 1–100 (standart 50, kattasi 100 ga tushiriladi). Noma'lum foydalanuvchi uchun {"conversationId":null,"items":[]}. Ichki izohlar hech qachon chiqmaydi.

9. v1 cheklovlari

  • Faqat matnli xabarlar (fayl/rasm yo'q).
  • API kanal lid yaratmaydi; mijoz telefoni so'ralmaydi.
  • Smenada onlayn operator bo'lsa suhbat operatorga biriktiriladi va AI jim turadi ("faqat AI" rejimi keyingi bosqichda).
  • Mijoz konteksti faqat 24/7 inbox agentida ishlatiladi (kechki agentda emas) va operator panelida ko'rsatilmaydi.
  • Bitta kompaniyada 5 tagacha API kanal; kalit kanalga bog'lanmaydi (kompaniyadagi istalgan kanal channelId bilan).