Партнёрский API (B2B2C)

API для партнёров PayBot — программно регистрируйте своих пользователей как клиентов PayBot, создавайте платёжные ссылки и получайте подписанные webhook о платежах в едином endpoint.

Партнёрский API нужен, если у вас есть собственный продукт (например,
SaaS, маркетплейс или AI-сервис), и вы хотите встроить приём
Kaspi-платежей через PayBot. Все клиенты, которых вы зарегистрируете
через API, автоматически закрепляются за вашим партнёрским
профилем — без реф-ссылок и приглашений.

Архитектура (B2B2C)

code
ваш сервис (neuroai.kz)            PayBot                  Kaspi
─────────────────────────         ──────                  ─────
POST /partner/v1/clients   →  Client(partner_id=ВЫ)
GET login_url              →  пользователь логинится
                              + подключает Kaspi          ← OAuth/Kaspi
POST /partner/v1/checkout  →  PaymentLink + QR            → создать QR
редирект на url            →  hosted checkout
                              ↑ оплата                    ← polling
webhook → ваш URL          ←  payment.completed
  • Один партнёрский ключ → доступ к данным всех ваших клиентов.
  • Один webhook URL → события всех ваших клиентов прилетают в одну точку с client.id и external_id внутри.
  • Каждый клиент получает свой кабинет PayBot, свой Kaspi-аккаунт, свои тарифы. Вы видите их в /partner/clients.

Получение ключа

1. Вы должны быть активным партнёром PayBot. Если ещё не подавали заявку — /partner/auth/register.

2. В кабинете /partner/integrationСоздать API ключ → выбрать live или test.

3. Скопируйте значение — оно показывается один раз.

Формат: pk_live_<32 hex> или pk_test_<32 hex>. Хранится только SHA-256 хеш.

Все запросы — с заголовком:

http
X-Partner-API-Key: pk_live_a1b2c3d4...

Базовый URL: https://api.paybot.kz

Проверка ключа

bash
Скачать
curl https://api.paybot.kz/partner/v1/ping \
  -H "X-Partner-API-Key: pk_live_..."
json
Скачать
{ "ok": true, "partner_id": 3, "partner_slug": "neuroai", "mode": "live" }

Регистрация клиента

Идемпотентный апсёрт по external_id (ваш внутренний ID пользователя).

Повторный вызов с тем же external_id вернёт уже созданный профиль

без ошибки.

bash
Скачать
curl -X POST https://api.paybot.kz/partner/v1/clients \
  -H "X-Partner-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ТОО Альфа",
    "email": "[email protected]",
    "phone": "+77071234567",
    "external_id": "u_42",
    "plan": "trial",
    "send_invite_email": false
  }'
json
Скачать
{
  "created": true,
  "client": {
    "id": 178,
    "public_id": "8d7a1f7c-...",
    "name": "ТОО Альфа",
    "email": "[email protected]",
    "phone": "+77071234567",
    "partner_external_id": "u_42",
    "plan": "trial",
    "plan_active": true,
    "plan_expires_at": "2026-05-24T12:00:00Z",
    "kaspi_connected": false,
    "is_active": true,
    "created_at": "2026-05-21T12:00:00Z"
  },
  "login_url": "https://paybot.kz/api/auth/magic?token=eyJhbGc...",
  "onboarding_url": "https://paybot.kz/dashboard/kaspi-connect"
}
  • login_url — одноразовая 15-минутная ссылка. Отправьте её

пользователю — он попадёт прямо в кабинет.

  • onboarding_url — куда направить клиента после входа, чтобы он

подключил свой Kaspi-аккаунт.

Создание чекаута для клиента возможно только после подключения Kaspi.
Поле kaspi_connected подсказывает текущее состояние.

Магик-ссылка для существующего клиента

bash
Скачать
curl -X POST https://api.paybot.kz/partner/v1/clients/u_42/login-link \
  -H "X-Partner-API-Key: pk_live_..."
json
Скачать
{ "login_url": "https://paybot.kz/api/auth/magic?token=...", "expires_at": "..." }

В пути допускается как PayBot ID (число), так и ваш external_id (строка).

Список клиентов

bash
Скачать
curl "https://api.paybot.kz/partner/v1/clients?limit=50&amp;plan=trial" \
  -H "X-Partner-API-Key: pk_live_..."

Cursor-пагинация: next_cursor + has_more. Фильтры: external_id, plan.

Hosted Checkout

Когда клиент подключил Kaspi, вы можете создать платёжную ссылку.

Сценарий — редирект пользователя из вашего интерфейса на

paybot.kz/checkout/, где он увидит брендированную страницу

оплаты с QR + кнопкой Kaspi.

bash
Скачать
curl -X POST https://api.paybot.kz/partner/v1/checkout \
  -H "X-Partner-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "u_42",
    "amount": 5000,
    "description": "Подписка neuroai PRO",
    "success_url": "https://neuroai.kz/billing/success",
    "cancel_url": "https://neuroai.kz/billing/cancel",
    "expires_in_minutes": 60
  }'
json
Скачать
{
  "id": 1027,
  "token": "pl_z9XyABCdEf",
  "url": "https://paybot.kz/checkout/pl_z9XyABCdEf",
  "qr_image_url": "https://api.qrserver.com/...",
  "amount": 5000,
  "description": "Подписка neuroai PRO",
  "status": "active",
  "expires_at": "2026-05-21T13:00:00Z",
  "client_id": 178,
  "external_id": "u_42"
}

Редиректите пользователя на url. После оплаты он попадёт на

success_url, а вам прилетит webhook payment.completed.

Ошибка 422 kaspi_not_connected — клиент ещё не подключил Kaspi.

Отправьте ему onboarding_url / новую магик-ссылку.

Webhook

Настройка

В кабинете /partner/integrationWebhook → введите URL и

выберите события. Секрет показывается один раз.

Или через API кабинета (JWT-авторизация):

bash
Скачать
curl -X PUT "https://api.paybot.kz/partner/integration/webhook?mode=live" \
  -H "Authorization: Bearer &lt;ваш партнёрский JWT&gt;" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://neuroai.kz/api/paybot/webhook",
    "events": ["payment.completed", "payment.expired", "client.created"],
    "is_active": true
  }'

Формат события

PayBot отправляет HTTPS POST на ваш URL:

ЗаголовокОписание
X-PayBot-Event-Idpwh_evt_abc123 — уникальный ID события
X-PayBot-Eventимя события (например payment.completed)
X-PayBot-Timestampunix-секунды на момент отправки
X-PayBot-Signaturesha256= — HMAC-SHA256(secret, "{timestamp}.{body}")

Тело — JSON:

json
Скачать
{
  "id": "pwh_evt_4c6f0e2b1a",
  "event": "payment.completed",
  "mode": "live",
  "partner_id": 3,
  "created_at": "2026-05-21T12:34:56Z",
  "data": {
    "client": {
      "id": 178,
      "external_id": "u_42",
      "email": "[email protected]"
    },
    "payment": {
      "operation_id": "QR-9F8E7D6C",
      "type": "qr",
      "amount": 5000,
      "status": "paid",
      "description": "Подписка neuroai PRO",
      "sender_name": "ИВАНОВ И. И.",
      "transaction_id": "TX-2025...",
      "paid_at": "2026-05-21T12:34:55Z"
    }
  }
}

Проверка подписи

python
Скачать
import hmac, hashlib, time

def verify(secret: str, timestamp: str, body: str, sig: str) -&gt; bool:
    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{body}".encode(),
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(f"sha256={expected}", sig):
        return False
    if abs(int(time.time()) - int(timestamp)) &gt; 300:  # 5 min skew
        return False
    return True
javascript
Скачать
import crypto from "node:crypto";

export function verifyPayBotSignature(secret, timestamp, body, signature) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${body}`)
    .digest("hex");
  if (!crypto.timingSafeEqual(Buffer.from(`sha256=${expected}`), Buffer.from(signature))) {
    return false;
  }
  return Math.abs(Date.now() / 1000 - Number(timestamp)) &lt; 300;
}

Retry

Если ваш endpoint вернул не 2xx — PayBot повторит до 5 раз с

задержками 0s → 30s → 5min → 30min → 2h. После 5-й попытки

webhook помечается как failed, а сам endpoint — failing_since

(нужно перенастроить вручную, иначе следующие события не отправятся).

Доступные события

СобытиеКогда
client.createdВы создали клиента через POST /partner/v1/clients
client.kaspi_connectedКлиент впервые подключил Kaspi
client.plan_changedТариф клиента сменился
payment.completedОплата успешно прошла
payment.cancelledПлатёж отменён пользователем или Kaspi
payment.expiredQR/счёт истёк без оплаты
payment.refundedВозврат проведён
partner.webhook.testТестовое событие из кабинета

Тестовая отправка

bash
Скачать
curl -X POST "https://api.paybot.kz/partner/integration/webhook/test?mode=live" \
  -H "Authorization: Bearer &lt;partner JWT&gt;" \
  -H "Content-Type: application/json" \
  -d '{"event_type": "partner.webhook.test"}'

Журнал доставок

/partner/integration/webhook/deliveries — последние 50 отправок с

кодами ответа, ошибками и временем доставки. Используйте для

диагностики «event пришёл / не пришёл».

Платежи

bash
Скачать
# Список платежей по всем клиентам партнёра
curl "https://api.paybot.kz/partner/v1/payments?status=paid&amp;limit=100" \
  -H "X-Partner-API-Key: pk_live_..."

# Один конкретный платёж
curl "https://api.paybot.kz/partner/v1/payments/QR-9F8E7D6C" \
  -H "X-Partner-API-Key: pk_live_..."

# Только платежи одного клиента
curl "https://api.paybot.kz/partner/v1/payments?client_ref=u_42" \
  -H "X-Partner-API-Key: pk_live_..."

Сценарий: SaaS-биллинг под Kaspi

python
Скачать
import requests

PAYBOT = "https://api.paybot.kz"
HEAD   = {"X-Partner-API-Key": "pk_live_..."}

# 1. При регистрации в neuroai
def on_signup(user):
    r = requests.post(f"{PAYBOT}/partner/v1/clients", json={
        "name": user.full_name,
        "email": user.email,
        "external_id": str(user.id),
        "plan": "trial",
    }, headers=HEAD).json()
    user.paybot_id = r["client"]["id"]
    # Отправьте r["login_url"] клиенту на email или покажите кнопку
    send_email(user.email, "Привяжите Kaspi", r["login_url"])

# 2. Пользователь нажал "Оплатить тариф" на neuroai.kz
def on_pay_click(user, amount):
    r = requests.post(f"{PAYBOT}/partner/v1/checkout", json={
        "external_id": str(user.id),
        "amount": amount,
        "description": f"Подписка neuroai PRO",
        "success_url": f"https://neuroai.kz/billing/{user.id}/success",
        "cancel_url":  f"https://neuroai.kz/billing/{user.id}/cancel",
    }, headers=HEAD).json()
    if r.get("status") != "active":
        raise RuntimeError("checkout creation failed")
    return r["url"]  # Редирект пользователя сюда

# 3. Когда оплата пришла — webhook payment.completed:
def on_webhook(event):
    if event["event"] != "payment.completed":
        return
    p = event["data"]["payment"]
    c = event["data"]["client"]
    activate_subscription(user_id=c["external_id"], amount=p["amount"])

Бесшовная (white-label) интеграция

Если вы хотите, чтобы конечный пользователь никогда не видел paybot.kz,

все шаги онбординга и настройки можно сделать через ваш собственный UI.

В сухом остатке: ваш бэкенд вызывает PayBot API, ваш фронт отображает

формы под вашим брендом, клиент даже не знает о существовании PayBot.

Подключение Kaspi через ваш UI

Никаких редиректов. Две формы в вашем интерфейсе → два HTTP-вызова.

bash
Скачать
# 1. Клиент ввёл свой номер Kaspi
curl -X POST https://api.paybot.kz/partner/v1/clients/u_42/kaspi/start \
  -H "X-Partner-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"phone": "+77071234567"}'
# → SMS улетела клиенту от KaspiBank
json
Скачать
{ "state": "awaiting_otp", "phone": "+77071234567", "connected": false }
bash
Скачать
# 2. Клиент ввёл код из SMS
curl -X POST https://api.paybot.kz/partner/v1/clients/u_42/kaspi/verify \
  -H "X-Partner-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"code": "1234"}'
json
Скачать
{
  "connected": true,
  "phone": "+77071234567",
  "org_name": "ИП Иванов И.И.",
  "org_bin": "880101300123"
}

Дополнительно:

bash
Скачать
# Переотправить SMS (если первая не пришла)
POST /partner/v1/clients/{ref}/kaspi/resend

# Проверить текущее состояние (idle | awaiting_otp | onboarded)
GET  /partner/v1/clients/{ref}/kaspi/status

Лимит на SMS: не более 3 SMS на один номер за 10 минут.

После успешного verify PayBot отправит вам webhook client.kaspi_connected

(если он включён в подписке) с полями client.id, external_id, kaspi.phone, kaspi.org_name.

Webhook от лица конкретного клиента

В дополнение к партнёрскому webhook (один на всех клиентов), вы можете

зарегистрировать отдельный webhook на каждого клиента — например, если

вы реплицируете каждого клиента отдельным endpoint'ом, или передаёте

webhook URL обратно в его собственный сервис.

bash
Скачать
# Создать
curl -X POST https://api.paybot.kz/partner/v1/clients/u_42/webhooks \
  -H "X-Partner-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://neuroai.kz/api/tenant/u_42/paybot",
    "events": ["payment.completed", "payment.refunded"],
    "description": "tenant feed"
  }'
json
Скачать
{
  "id": 17,
  "url": "https://neuroai.kz/api/tenant/u_42/paybot",
  "events": ["payment.completed", "payment.refunded"],
  "description": "tenant feed",
  "is_active": true,
  "failing_since": null,
  "created_at": "...",
  "secret": "whsec_..."
}

Секрет показывается один раз. Подпись и retry — те же, что у клиентского

webhook (см. Webhooks).

bash
Скачать
# Список
GET    /partner/v1/clients/{ref}/webhooks

# Удалить
DELETE /partner/v1/clients/{ref}/webhooks/{webhook_id}

API-ключ клиента через партнёрский API

Если ваш сценарий предполагает, что клиент сам будет делать прямые

запросы к PayBot (через ваш SDK), вы можете программно ротировать его

ключ:

bash
Скачать
# Узнать префикс активного ключа (полный — не покажем)
GET /partner/v1/clients/u_42/api-key
# → {"prefix": "kp_live_8f3a", "mode": "live"}

# Сгенерировать новый ключ (старый сразу перестаёт работать)
POST /partner/v1/clients/u_42/api-key/rotate
json
Скачать
{
  "prefix": "kp_live_e21c",
  "mode": "live",
  "api_key": "kp_live_e21c4a8f5c2b9d1e7..."
}

Полный ключ возвращается только на rotate — больше его восстановить

нельзя. Передайте его клиенту через защищённый канал (или сохраните в

своей секретнице, если клиент пользуется PayBot только через ваш SaaS).

Полный headless flow на Python

python
Скачать
import requests

PAYBOT = "https://api.paybot.kz"
HEAD   = {"X-Partner-API-Key": "pk_live_..."}

def onboard_user(user, kaspi_phone, otp_code):
    # 1. Регистрация (идемпотентна по external_id)
    requests.post(f"{PAYBOT}/partner/v1/clients", json={
        "name": user.full_name,
        "email": user.email,
        "external_id": str(user.id),
    }, headers=HEAD)

    # 2. SMS на номер Kaspi
    requests.post(
        f"{PAYBOT}/partner/v1/clients/{user.id}/kaspi/start",
        json={"phone": kaspi_phone}, headers=HEAD,
    )
    # ... ждём, пока клиент введёт код в форме у нас ...

    # 3. Верификация OTP
    r = requests.post(
        f"{PAYBOT}/partner/v1/clients/{user.id}/kaspi/verify",
        json={"code": otp_code}, headers=HEAD,
    ).json()
    assert r["connected"]

    # 4. (Опционально) персональный webhook на этого клиента
    requests.post(
        f"{PAYBOT}/partner/v1/clients/{user.id}/webhooks",
        json={
            "url": f"https://neuroai.kz/paybot/{user.id}",
            "events": ["payment.completed"],
        },
        headers=HEAD,
    )

    # 5. Готово — теперь можно делать checkout
    co = requests.post(f"{PAYBOT}/partner/v1/checkout", json={
        "external_id": str(user.id),
        "amount": 5000,
        "description": "Тариф PRO",
    }, headers=HEAD).json()
    return co["url"]

Юридический момент

При полностью headless-сценарии конечный пользователь формально не

принимает Terms of Service PayBot. Партнёр (вы) принимаете их за всех

своих клиентов в рамках партнёрского соглашения. Если клиент захочет

вывести заработанное на свой банковский счёт — он всё равно один раз

должен будет зайти в paybot.kz для KYC + ввода реквизитов. Для этого

используйте POST /partner/v1/clients/{ref}/login-link.

Лимиты и ограничения

  • Rate limit: 240 req/min на один (партнёр, mode). Под нагрузкой —

напишите в поддержку, поднимем лимит.

  • Идемпотентность: POST /partner/v1/clients идемпотентен по

external_id. Для POST /partner/v1/checkout поддерживается

заголовок Idempotency-Key.

  • Тестовый режим: ключ pk_test_* не дёргает Kaspi и не создаёт

реальных списаний. Используйте для разработки и CI.

  • Безопасность ключей: ключ нельзя восстановить. Если утёк —

отзовите через /partner/integration/api-keys/{id} (DELETE) и

выпустите новый. Хранить в .env/секретнице вашего сервиса.

FAQ

Можно ли использовать один API-ключ из нескольких сервисов?

Технически — да, но мы рекомендуем выпустить отдельный ключ для каждой

среды (prod / staging / CI), чтобы быстрее локализовать утечку.

Что если я создам клиента, который уже зарегистрирован сам в PayBot?

Если этот клиент ещё не привязан к какому-либо партнёру и его email

совпадает — вы получите 409 client_email_taken. Используйте

процедуру claim для

запроса на привязку существующего клиента.

Как получить полную выписку платежей за период?

Через /partner/v1/payments с пагинацией. Для CSV-выгрузки —

используйте кабинет /partner/clients.

Webhook за партнёра приходит, но не приходит клиенту — это нормально?

Да. Если конкретный клиент не настроил свой собственный webhook

(/dashboard/webhooks) — событие уходит только вам как партнёру.

Если у клиента настроен — оба endpoint получат своё уведомление.