Если вы мерчант и хотите принимать платежи через Kaspi Pay в своём бизнесе — вам нужна обычная документация:
Partner API использует X-Partner-Key и предназначен для server-to-server
интеграций, когда вы онбордите сторонних мерчантов в ApiPay от своего имени.
Server-to-server API для партнёров-интеграторов (CRM, маркетплейсы, white-label-кассы).
Партнёр онбордит своих мерчантов, авторизует их Kaspi-кассы и выдаёт им платёжный ключ
X-API-Key — всё программно, из своей системы.
Платёжные операции, webhook-события и проверка HMAC живут в документации для мерчантов (/docs) и здесь не дублируются. Привязку кассира перед боевым подключением можно прогнать в песочнице партнёра — детерминированно, без единого реального вызова Kaspi и без SMS.
Если вы разрабатываете CRM-систему, платёжную платформу, агрегатор или white-label-кассу
и хотите подключать своих клиентов к приёму Kaspi Pay-платежей — Partner API ApiPay создан
именно для этого. Вы получаете единый X-Partner-Key → создаёте организацию
мерчанта → авторизуете кассира через Kaspi-SMS → выпускаете её X-API-Key.
Дальше мерчант создаёт счета по документации для мерчантов /docs.
| Поверхность | Базовый URL |
|---|---|
| Partner API (онбординг, выдача X-API-Key) | https://api.apipay.kz/api/partner |
| Платёжный API мерчанта (счета, lookup, webhooks) — см. /docs | https://api.apipay.kz/api/v1 |
Релевантны API ровно два ключа.
| Ключ / заголовок | Кто владеет | Для чего |
|---|---|---|
X-Partner-Key |
партнёр | server-to-server: создание организаций мерчантов, авторизация кассира, мониторинг своих организаций, выдача X-API-Key мерчанту. Берётся в партнёрском кабинете. |
X-API-Key |
конкретный мерчант (вы выдаёте ключ через Partner API) | платёжные операции от имени мерчанта (счета, статусы, возвраты, lookup) — описаны в документации для мерчантов /docs, здесь не дублируются. |
X-Partner-Key выдаётся в партнёрском кабинете; ротация ключа и переключение
sandbox/production — тоже в кабинете (фронт), не через API.
X-Partner-Key)
Префикс /api/partner. Лимит группы — 120 req/min на партнёра.
Server-to-server: создавайте организации мерчантов, авторизуйте кассира, мониторьте свои
организации и выдавайте X-API-Key мерчанту.
/api/partner/organizations — создать организацию мерчантаИдемпотентно по external_id — повторный POST вернёт существующую org.
Лимит 10/min. Ответ 201 (создана) / 200 (идемпотентный повтор).
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
has_catalog | boolean | нет | Создать организацию с каталогом товаров |
external_id | string | нет | Ваш идентификатор клиента в CRM (ключ идемпотентности) |
curl -X POST https://api.apipay.kz/api/partner/organizations \
-H 'X-Partner-Key: <X-Partner-Key>' -H 'Content-Type: application/json' \
-d '{"has_catalog":false,"external_id":"crm-client-42"}'
{ "success": true, "organization": {
"id": 50, "name": "ТОО Example", "idn": "123456789012",
"status": "verified", "sandbox_mode": true, "has_catalog": false,
"kaspi_connected": true, "session_mode": "self", "external_id": "crm-client-42",
"origin": "created", "payment_status": "active", "has_active_payment": true,
"created_at": "2026-05-16T10:00:00+05:00" } }
Опциональное поле name (max 255): пусто → автоген PARTNER_<id>_<ts>, позже подменяется именем из Kaspi на verify-otp.
/api/partner/organizations — список организаций (мониторинг)Мониторинг своих организаций: список с пагинацией. В карточке есть origin.
Query: per_page (1–100, def 25), page (def 1),
status (pending|verified|suspended), sandbox_mode (true/false/1/0),
search (LIKE-поиск по name / external_id / idn, max 255).
Фильтры применяются до пагинации. Ключ data — алиас organizations (back-compat).
{ "success": true, "organizations": ["<card>"], "data": ["<card>"],
"current_page": 1, "per_page": 25, "total": 42, "last_page": 2 }
/api/partner/organizations/{id} — карточка организацииПолучить карточку конкретной организации. → { "success": true, "organization": <card> }
/api/partner/organizations/{id} — отвязать организациюДеактивирует все API-ключи org и делает soft-delete. → { "success": true }
/api/partner/organizations/{id}/api-key — выдать X-API-Key мерчанту
Создать/перегенерировать ключ мерчанта + webhook. webhook_url проходит
SSRF-валидацию (приватные IP → 422). Идемпотентно (повтор = перегенерация).
Дальше мерчант работает этим ключом по документации для мерчантов
(/docs).
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
name | string | нет | Произвольное название ключа |
webhook_url | string | да | URL для webhook-уведомлений мерчанта |
webhook_secret | string | нет | Секрет подписи (генерируется, если не указан) |
curl -X POST https://api.apipay.kz/api/partner/organizations/501/api-key \
-H 'X-Partner-Key: <X-Partner-Key>' -H 'Content-Type: application/json' \
-d '{"name":"CRM key","webhook_url":"https://crm.example.kz/sub/501/webhook"}'
{ "success": true, "key": "<X-API-Key, один раз>", "key_id": 900,
"webhook_url": "https://crm.example.kz/sub/501/webhook",
"webhook_secret": "whsec_yyyy", "is_org_default": true, "regenerated": false }
Префикс /api/partner/organizations/{id}/kaspi-auth. process_id
живёт 10 минут — шаги send-phone и verify-otp нужно выполнить в
этом окне. Боевая org доступна только production-партнёру (иначе
403 production_access_required); тестовая org всегда идёт мок-путём.
.../kaspi-auth/init — Шаг 1Инициировать авторизацию кассира Kaspi. Body {} (опц. { "force": true }).
force: true — переавторизация поверх активной сессии (смена кассира). Без
force по уже подключённой org → 409 already_connected.
{ "success": true, "process_id": "SANDBOX-<uuid>", "process_status": "phone_required" }
.../kaspi-auth/send-phone — Шаг 2Body { "cashier_phone": "7XXXXXXXXXX" }. Kaspi отправляет SMS на номер кассира.
Успех → { "success": true, "process_status": "otp_required" }.
Ошибки: invalid_phone (422 — неверный формат), not_cashier
(422 — номер не «чистый кассир»), not_registered (422 — не зарегистрирован кассиром в Kaspi),
no_process (409 — нет активного процесса: вызовите init / он истёк),
context_expired (409 — Kaspi-контекст протух, ~10 мин: init заново + повтор send-phone),
org_claim_conflict (409 — касса уже привязана к другому владельцу),
sms_failed (502), kaspi_busy (503 — Kaspi недоступен; заголовок Retry-After: 60).
В sandbox not_cashier / sms_failed / not_registered /
context_expired / kaspi_busy эмулируются магическими номерами (см. раздел Sandbox).
.../kaspi-auth/verify-otp — Шаг 3Body { "otp": "0000" } (4–6 цифр). Подтвердить код из SMS.
200: { "success": true, "mode": "self", "organization": <card>, "process_status": "active" } — org → status: verified.200: { "success": false, "error": "invalid_otp", "process_status": "otp_required" } — повторяемо, сессия жива.org_claim_conflict (409): Kaspi-кассир/организация уже привязаны к другой организации в ApiPay.0000 = успех, любой другой = invalid_otp..../kaspi-auth/status — статусТекущий статус авторизации кассира. status: none | pending | active | expired.
process_status: idle | phone_required | otp_required | active | failed (выводится из persisted-сессии).
{ "success": true, "status": "active", "process_status": "active",
"kaspi_connected": true, "expires_at": "2026-06-16T00:00:00+05:00" }
<card>| Поле | Тип | Описание |
|---|---|---|
id | number | ID организации |
name | string | Название |
idn | string | БИН/ИИН |
status | string | pending | verified | suspended |
sandbox_mode | boolean | Режим песочницы |
has_catalog | boolean | Есть каталог |
kaspi_connected | boolean | Касса Kaspi подключена |
session_mode | string | Режим привязки (self) |
external_id | string | Ваш CRM-идентификатор |
origin | string | referral | created | claimed — происхождение орги (никогда не null) |
payment_status | string | none | active | expired |
payment_expires_at | string|null | Когда истекает тариф |
has_active_payment | boolean | Активная оплата |
created_at | string | Дата создания |
{
"id": 50, "name": "ТОО Example", "idn": "123456789012",
"status": "pending|verified|suspended",
"sandbox_mode": false, "has_catalog": false, "kaspi_connected": true,
"session_mode": "self", "external_id": "crm-client-42",
"origin": "referral|created|claimed",
"payment_status": "none|active|expired",
"payment_expires_at": "2026-06-16T00:00:00+05:00",
"has_active_payment": false, "created_at": "2026-05-16T10:00:00+05:00"
}
Партнёр сам платит ApiPay за подписку подключённого мерчанта
(start/business/pro) и мониторит её. Оплата —
счётом через Kaspi: телефон плательщика в теле, тариф активируется
асинхронно после оплаты (webhook invoice.status_changed → paid).
Это подписочная плата мерчант→ApiPay, не оборот мерчанта.
/api/partner/tariff-plans — каталог тарифовОбщий каталог (без привязки к орг): 3 тарифа + 12 планов (3 × 1/3/6/12 мес).
→ { "success": true, "tiers": [...], "plans": [...] }
/api/partner/organizations/{id}/tariff — статус подписки мерчантаСнимок подписки. Нет тарифа → status: "none" (не 404). next_payment.amount — всегда.
{ "success": true, "tariff": {
"status": "none|trial|active|expired", "tier": "business", "is_trial": false,
"started_at": "2026-06-20T10:00:00+05:00", "expires_at": "2026-09-20T10:00:00+05:00",
"days_remaining": 92, "auto_renew": false,
"last_payment": { "amount": 71250, "paid_at": "...", "period_months": 3, "tier": "business", "status": "completed" },
"next_payment": { "due_at": "2026-09-20T10:00:00+05:00", "amount": 71250, "tier": "business" } } }
/api/partner/organizations/{id}/tariff/pay — оплатить тарифBody: { "tier_id": "business", "period_months": 3, "phone": "8XXXXXXXXXX", "set_billing_phone": false }
(phone — плательщик, формат 8XXXXXXXXXX; НЕ кассир). Боевая org — только
production-партнёру (иначе 403 production_access_required); тестовая →
мгновенная мок-активация (201, status:"completed", self_api_invoice_id:null).
{ "success": true, "payment": {
"id": 42, "amount": 71250, "status": "pending", "tier": "business", "period_months": 3,
"self_api_invoice_id": 778899, "external_id": "partner-tariff-501-1716900000",
"paid_at": null, "expires_at": null, "created_at": "2026-06-20T10:00:00+05:00", "failure_reason": null } }
Ошибки: 403 production_access_required, 404 organization_not_found,
409 tariff_payment_pending (в теле — существующий payment), 422 invalid_tariff_plan,
429 tariff_payment_cooldown (+Retry-After, retry_after_seconds:1800),
503 tariff_payment_unavailable (+Retry-After, retry_after_seconds:30).
/api/partner/organizations/{id}/tariff/payments и /{paymentId}Список: { "success": true, "data": [ <payment> ] } (по убыванию даты, триальные amount=0 исключены).
Один: { "success": true, "payment": <payment> }; неизвестный paymentId → 404 tariff_payment_not_found.
<payment>: { id, amount, status, tier, period_months, self_api_invoice_id, external_id, paid_at, expires_at, created_at, failure_reason }.
/api/partner/health — health аккаунта партнёраАгрегат по всем орг (без proxy-деталей), кэш ~30с.
{ "success": true, "api": {"status":"ok"},
"organizations": {"total":12,"kaspi_connected":9,"needs_reauth":1,"tariff_active":7,"tariff_expired":2,"on_trial":3},
"webhooks": {"delivered_24h":340,"failed_24h":5,"success_rate":98.6},
"rate_limits": {"partner_api_per_min":120} }
{ "success": false, "error": "<code>", "error_code": "<code>", "message": "<ru>", "errors"?: {...} }
(error дублирует error_code для back-compat; errors — только на 422 из контроллера).
Middleware (аутентификация/владение/production-гейт): сокращённая форма { "success": false, "error": "<code>" }.
Валидация FormRequest: стандартная Laravel-форма { "message", "errors" } без success/error_code.
Коды error_code стабильны — используйте для локализации.+05:00). Исключение: timestamp внутри webhook-payload — UTC (+00:00).{ "message": "Too Many Attempts." } + Retry-After.
Тарифные лимиты (tariff/pay) кладут в тело retry_after_seconds (1800/30).
Песочница партнёра детерминированно эмулирует привязку кассира: ни одного
реального вызова Kaspi, ни одной SMS. Магические номера на send-phone подменяют
ответ Kaspi на фиксированный; любой другой валидный номер 7XXXXXXXXXX трактуется
как «обычный» (success). Тестовая организация архитектурно не может стать боевой.
simulate-status) — это песочница мерчанта,
отдельная система: см. документацию для мерчантов /docs.
Магические значения (только привязка кассира):
| Шаг / магическое значение | Результат | HTTP |
|---|---|---|
kaspi-auth/init | process_id = "SANDBOX-<uuid>" | 200 |
send-phone 77770000010 | success (касса привязывается) | 200 |
send-phone 77770000011 | not_cashier — номер не кассир Kaspi | 422 |
send-phone 77770000012 | sms_failed — Kaspi не смог отправить SMS | 502 |
send-phone 77770000013 | not_registered — номер не зарегистрирован кассиром в Kaspi | 422 |
send-phone 77770000014 | context_expired — Kaspi-контекст протух (повторите init) | 409 |
send-phone 77770000015 | kaspi_busy — Kaspi временно недоступен (Retry-After: 60) | 503 |
send-phone прочий валидный 7… | success | 200 |
verify-otp 0000 | success → org status: verified (sandbox_mode остаётся true) | 200 |
verify-otp любой другой код | invalid_otp (повторяемо, сессия жива) | 200 |
POST /organizations {"external_id":"test-1"} → org.idPOST .../{id}/kaspi-auth/init → process_idPOST .../send-phone {"cashier_phone":"77770000010"} → successPOST .../verify-otp {"otp":"0000"} → org status: verifiedPOST .../{id}/api-key → X-API-Key мерчанта (дальше — по /docs)POST .../send-phone {"cashier_phone":"77770000011"} → 422 not_cashierPOST .../send-phone {"cashier_phone":"77770000012"} → 502 sms_failedPOST .../send-phone {"cashier_phone":"77770000013"} → 422 not_registeredPOST .../send-phone {"cashier_phone":"77770000014"} → 409 context_expiredinit заново и повторите send-phone (один раз).POST .../send-phone {"cashier_phone":"77770000015"} → 503 kaspi_busy (заголовок Retry-After: 60)Retry-After секунд, покажите «Kaspi временно недоступен».POST .../send-phone {"cashier_phone":"77770000010"} → successPOST .../verify-otp {"otp":"1234"} → { "success": false, "error": "invalid_otp" } — повторяемо0000 — сессия остаётся живой.| Лимит | Значение | Превышение |
|---|---|---|
| Тестовых организаций на партнёра | 20 | 429 test_org_limit |
| Создание организаций | 10 req/min | throttle |
| kaspi-auth (тестовая org) | 60 req/min | throttle (на партнёра+org) |
| kaspi-auth (боевая org) | 10 req/min | throttle (на партнёра+org) |
| Вся группа Partner API | 120 req/min | throttle (на партнёра) |
Партнёрский онбординг до выдачи X-API-Key:
X-Partner-Key в партнёрском кабинете (фронт).POST /api/partner/organizations {external_id} → org.idPOST .../{id}/kaspi-auth/init → process_idPOST .../{id}/kaspi-auth/send-phone {"cashier_phone":"77770000010"} → successPOST .../{id}/kaspi-auth/verify-otp {"otp":"0000"} → org status: verifiedPOST .../{id}/api-key → X-API-Key мерчанта + webhook_secretX-API-Key — по документации для мерчантов (/docs).GET /api/partner/organizations.
Партнёр задаёт webhook_url при выдаче X-API-Key. Формат событий и
проверка HMAC-подписи описаны в документации для мерчантов
(/docs) — здесь не дублируются.
X-Partner-Key выдаётся в партнёрском кабинете (фронт). Sandbox — сразу
self-service. Production (реальный Kaspi-auth и mode=production) — после ручного
одобрения админом; переключение режима тоже в кабинете.
Онбординг мерчанта и привязку кассира — детерминированно, без реального Kaspi и SMS (магические номера). Тестирование счетов/возвратов/webhooks — это отдельная песочница мерчанта, см. /docs.
В документации для мерчантов /docs. Партнёр задаёт
webhook_url при выдаче X-API-Key, а формат событий и проверку HMAC
мерчант (и ваш сервер) берёт из /docs.
Оформите партнёрский статус на сайте — sandbox-доступ и X-Partner-Key
выдаются в партнёрском кабинете. Прогоните привязку кассира без единого реального вызова
Kaspi, затем запросите production-доступ у админа.
Подать заявку на партнёрство →
Поддержка: WhatsApp +7 708 516 74 89