Полная документация ApiPay REST API v2 — Автоматизация Kaspi Pay (Phone Payments)
REST API для приёма платежей по номеру телефона через Kaspi Pay. Чеки через Kaspi ОФД, webhooks, поддержка каталога, подписок и возвратов. Без скрытых комиссий.
Подключаете через ИИ-ассистента?
Начните с пошагового плейбука интеграции — apipay.kz/for-ai:
весь сценарий (подключение кассира, ключи, код, вебхуки, отладка) в одном документе.
Быстрый старт
Песочница доступна сразу! При регистрации создаётся sandbox-организация для тестирования API. Вы можете создавать тестовые счета (is_sandbox=true) до подключения Kaspi Business. Переключение в production — через личный кабинет.
Для работы с реальными платежами подключите кассира Kaspi: самостоятельно в личном кабинете (Настройки → Авторизация Kaspi, около 1 минуты) или через WhatsApp поддержки
Настройте webhook в личном кабинете для получения уведомлений об оплате
Базовая конфигурация
Параметр
Значение
Base URL
https://bpapi.bazarbay.site/api/v1
Аутентификация
Header X-API-Key: ваш_api_ключ
Rate Limit
200 req/min на API-ключ (общий лимит Public API; при превышении 429 + Retry-After)
Content-Type
application/json
Обзор эндпоинтов
#
Метод
Путь
Описание
Счета (Invoices)
1
POST
/invoices
Создать счёт
2
POST
/invoices/qr
Создать QR-счёт
3
GET
/invoices
Список счетов
4
GET
/invoices/{id}
Просмотр счёта
5
POST
/invoices/{id}/cancel
Отменить счёт
6
POST
/invoices/status/check
Проверить статусы счетов
Other
7
POST
/clients/check
Проверить номер клиента в Kaspi
Возвраты (Refunds)
8
POST
/invoices/{id}/refund
Возврат по счёту
9
GET
/invoices/{id}/refunds
Список возвратов по счёту
10
GET
/refunds
Список возвратов
Каталог (Catalog)
11
GET
/catalog/units
Список единиц измерения
12
GET
/catalog
Список товаров каталога
13
POST
/catalog/upload-image
Загрузить изображение для каталога
14
POST
/catalog
Создать товар каталога
15
PATCH
/catalog/{id}
Обновить товар каталога
16
DELETE
/catalog/{id}
Удалить товар каталога
Подписки (Subscriptions)
17
POST
/subscriptions
Создать подписку
18
GET
/subscriptions
Список подписок
19
GET
/subscriptions/{id}
Просмотр подписки
20
PUT
/subscriptions/{id}
Обновить подписку
21
POST
/subscriptions/{id}/pause
Приостановить подписку
22
POST
/subscriptions/{id}/resume
Возобновить подписку
23
POST
/subscriptions/{id}/cancel
Отменить подписку
24
GET
/subscriptions/{id}/invoices
История платежей подписки
Health Check
Счета (Invoices)
GET /invoices
Возвращает пагинированный список счетов с возможностью фильтрации.
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.kaspi_invoice_id
string
—
—
ID счёта в системе Kaspi.
data.phone
string
—
—
Номер телефона клиента.
data.client_name
string
—
—
Имя клиента.
data.client_comment
string
—
да
Комментарий клиента (может быть null).
data.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
data.is_recurring
boolean
—
—
Признак счёта по подписке.
data.subtotal
string
—
да
Сумма до скидки (может быть null).
data.discount_sum
string
—
да
Сумма скидки (может быть null).
data.discount_percentage
string
—
да
Процент скидки (может быть null).
data.total_refunded
string
—
—
Суммарно возвращено по счёту.
data.is_fully_refunded
boolean
—
—
Признак полного возврата.
data.error_message
string
—
да
Текст ошибки (null если ошибок нет).
data.paid_at
string
—
—
Дата и время оплаты (null если не оплачено).
data.created_at
string
—
—
Дата и время создания.
data.items
any[]
—
—
Позиции (товары) объекта.
total
integer
—
—
Общее количество записей.
POST /invoices
Создаёт новый счёт для оплаты через Kaspi Pay.
Для организаций с каталогом используйте `cart_items` вместо `amount`.
Параметры запроса
Поле
Тип
Обяз.
Описание
phone_number
string
Да
Номер телефона. Формат: 8XXXXXXXXXX.
description
string
—
Описание счёта.
external_order_id
string
—
Внешний ID заказа.
amount
number
—
Сумма (обязательна без cart_items).
cart_items
object[]
—
Товары корзины (для организаций с каталогом).
cart_items.catalog_item_id
integer
Да
ID товара из каталога.
cart_items.count
integer
Да
Количество.
cart_items.price
number
—
Переопределённая цена за единицу. Если не указана — используется selling_price из каталога.
discount_percentage
number
—
Скидка на весь чек, 1-99% (только с cart_items).
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
id
integer
—
—
ID записи.
amount
string
—
—
Сумма в тенге.
status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
paid_at
string
—
да
Дата и время оплаты (null если не оплачено).
phone
string
—
—
Номер телефона клиента.
created_at
string
—
—
Дата и время создания.
GET /invoices/{id}
Возвращает детальную информацию о счёте, включая товары.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
id
integer
—
—
ID записи.
amount
string
—
—
Сумма в тенге.
description
string
—
—
Описание.
external_order_id
string
—
—
Внешний ID заказа.
status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
kaspi_invoice_id
string
—
—
ID счёта в системе Kaspi.
phone
string
—
—
Номер телефона клиента.
client_name
string
—
—
Имя клиента.
client_comment
string
—
да
Комментарий клиента (может быть null).
is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
is_recurring
boolean
—
—
Признак счёта по подписке.
subtotal
string
—
да
Сумма до скидки (может быть null).
discount_sum
string
—
да
Сумма скидки (может быть null).
discount_percentage
string
—
да
Процент скидки (может быть null).
total_refunded
string
—
—
Суммарно возвращено по счёту.
is_fully_refunded
boolean
—
—
Признак полного возврата.
error_message
string
—
да
Текст ошибки (null если ошибок нет).
error_code
string
—
да
Стабильный snake_case-код ошибки из каталога (раздел «Коды ошибок»). null, если ошибок нет. Стройте логику по этому коду, а не по тексту error_message.
paid_at
string
—
—
Дата и время оплаты (null если не оплачено).
created_at
string
—
—
Дата и время создания.
items
any[]
—
—
Позиции (товары) объекта.
POST /invoices/{id}/cancel
Отменяет счёт в статусе pending или processing. Для production счетов отмена выполняется асинхронно (статус 202).
После ответа 202 счёт переходит в статус `cancelling` — НЕ считайте его отменённым сразу. Итог придёт вебхуком `invoice.status_changed` (`cancelled` — отмена прошла, либо `error` — отменить не удалось). Если Kaspi отказал в отмене (как правило, счёт уже оплачен), счёт тихо вернётся в `pending`, а реальный статус (обычно `paid`) доставит синхронизация в течение нескольких минут. Дождитесь вебхука или опросите `GET /invoices/{id}`.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
invoice
object
—
—
Связанный счёт.
invoice.id
integer
—
—
ID записи.
invoice.amount
string
—
—
Сумма в тенге.
invoice.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
invoice.phone
string
—
—
Номер телефона клиента.
invoice.created_at
string
—
—
Дата и время создания.
POST /invoices/{id}/refund
Создаёт возврат по оплаченному счёту. Поддерживает полный и частичный возврат,
а также поэлементный возврат через `return_items`.
Ответ 201 означает, что возврат принят и поставлен в очередь (статус `pending`). Итог придёт вебхуком `invoice.refunded` (`completed` — возврат проведён, либо `failed` — возврат не удался, причина в `refund.error_code`). Дождитесь вебхука или опросите `GET /invoices/{id}/refunds`.
Параметры запроса
Поле
Тип
Обяз.
Описание
amount
number
—
Сумма возврата (без return_items; по умолчанию — вся доступная сумма).
reason
string
—
Причина возврата.
return_items
object[]
—
Товары для возврата (вместо amount).
return_items.catalog_item_id
integer
Да
ID товара из каталога.
return_items.count
integer
Да
Количество для возврата.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
refund
object
—
—
Объект возврата.
refund.id
integer
—
—
ID записи.
refund.invoice_id
integer
—
—
ID связанного счёта.
refund.user_id
integer
—
—
ID пользователя, инициировавшего операцию.
refund.api_key_id
integer
—
—
ID API-ключа.
refund.amount
string
—
—
Сумма в тенге.
refund.kaspi_refund_id
string
—
да
ID возврата в системе Kaspi (null до обработки).
refund.kaspi_status
string
—
да
Статус возврата в системе Kaspi (null до обработки).
refund.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
refund.reason
string
—
—
Причина возврата.
refund.initiated_by
string
—
—
Источник инициации: api_key или dashboard.
refund.error_message
string
—
да
Текст ошибки (null если ошибок нет).
refund.created_at
string
—
—
Дата и время создания.
invoice
object
—
—
Связанный счёт.
invoice.id
integer
—
—
ID записи.
invoice.amount
string
—
—
Сумма в тенге.
invoice.total_refunded
string
—
—
Суммарно возвращено по счёту.
invoice.available_for_refund
integer
—
—
Доступная для возврата сумма.
invoice.pending_refund_amount
integer
—
—
Сумма возвратов в обработке.
GET /invoices/{id}/refunds
Возвращает все возвраты по конкретному счёту.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
invoice
object
—
—
Связанный счёт.
invoice.id
integer
—
—
ID записи.
invoice.amount
string
—
—
Сумма в тенге.
invoice.total_refunded
string
—
—
Суммарно возвращено по счёту.
invoice.available_for_refund
integer
—
—
Доступная для возврата сумма.
invoice.is_fully_refunded
boolean
—
—
Признак полного возврата.
refunds
object[]
—
—
Список возвратов.
refunds.id
integer
—
—
ID записи.
refunds.invoice_id
integer
—
—
ID связанного счёта.
refunds.user_id
integer
—
—
ID пользователя, инициировавшего операцию.
refunds.api_key_id
integer
—
—
ID API-ключа.
refunds.amount
string
—
—
Сумма в тенге.
refunds.kaspi_refund_id
string
—
да
ID возврата в системе Kaspi (null до обработки).
refunds.kaspi_status
string
—
да
Статус возврата в системе Kaspi (null до обработки).
refunds.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
refunds.reason
string
—
—
Причина возврата.
refunds.initiated_by
string
—
—
Источник инициации: api_key или dashboard.
refunds.error_message
string
—
да
Текст ошибки (null если ошибок нет).
refunds.error_code
string
—
да
Стабильный snake_case-код ошибки возврата из каталога (раздел «Коды ошибок»). Заполнен только при status=failed (например refund_window_expired); иначе null. Стройте логику по этому коду, а не по тексту error_message.
refunds.created_at
string
—
—
Дата и время создания.
refunds.items
any[]
—
—
Позиции (товары) объекта.
total
integer
—
—
Общее количество записей.
POST /invoices/status/check
Диспатчит задачи проверки статусов для указанных счетов через Kaspi API.
Параметры запроса
Поле
Тип
Обяз.
Описание
invoice_ids
integer[]
Да
Массив ID счетов.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
invoices
object[]
—
—
Список счетов с актуальными статусами.
invoices.id
integer
—
—
ID записи.
invoices.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
invoices.kaspi_invoice_id
string
—
—
ID счёта в системе Kaspi.
invoices.amount
string
—
—
Сумма в тенге.
invoices.error_message
string
—
да
Текст ошибки (null если ошибок нет).
invoices.updated_at
string
—
—
Дата и время последнего обновления.
Возвраты (Refunds)
GET /refunds
Возвращает список возвратов организации с фильтрацией и пагинацией.
Параметры запроса (query)
Поле
Тип
Обяз.
Описание
status[]
string[]
—
Фильтр по статусам. Допустимые значения: `pending`, `processing`, `completed`, `failed`.
invoice_id
integer
—
Фильтр по ID счёта.
date_from
string
—
Начало периода в формате `Y-m-d`.
date_to
string
—
Конец периода в формате `Y-m-d`.
per_page
integer
—
Количество записей на страницу (1-100).
Параметры запроса
Поле
Тип
Обяз.
Описание
status
string[]
—
invoice_id
integer
—
The <code>id</code> of an existing record in the invoices table.
date_from
string
—
Must be a valid date. Must be a valid date in the format <code>Y-m-d</code>.
date_to
string
—
Must be a valid date. Must be a valid date in the format <code>Y-m-d</code>. Must be a date after or equal to <code>date_from</code>.
per_page
integer
—
Must be at least 1. Must not be greater than 100.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
current_page
integer
—
—
Номер текущей страницы.
data
object[]
—
—
Массив записей результата.
data.id
integer
—
—
ID записи.
data.invoice_id
integer
—
—
ID связанного счёта.
data.user_id
integer
—
—
ID пользователя, инициировавшего операцию.
data.api_key_id
integer
—
—
ID API-ключа.
data.amount
string
—
—
Сумма в тенге.
data.kaspi_refund_id
string
—
да
ID возврата в системе Kaspi (null до обработки).
data.kaspi_status
string
—
да
Статус возврата в системе Kaspi (null до обработки).
data.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.reason
string
—
—
Причина возврата.
data.initiated_by
string
—
—
Источник инициации: api_key или dashboard.
data.error_message
string
—
да
Текст ошибки (null если ошибок нет).
data.created_at
string
—
—
Дата и время создания.
data.invoice
object
—
—
Связанный счёт.
data.items
any[]
—
—
Позиции (товары) объекта.
total
integer
—
—
Общее количество записей.
Каталог (Catalog)
GET /catalog/units
Возвращает список доступных единиц измерения для товаров каталога (шт, кг, л и т.д.).
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
data
object[]
—
—
Массив записей результата.
data.id
integer
—
—
ID записи.
data.name
string
—
—
Название.
data.name_kaz
string
—
—
Название на казахском языке.
GET /catalog
Возвращает пагинированный список товаров каталога организации. Поддерживает поиск и фильтрацию.
ID товара в системе Kaspi (null до синхронизации).
data.name
string
—
—
Название.
data.unit_id
integer
—
—
ID единицы измерения.
data.selling_price
string
—
—
Цена продажи.
data.date_added
string
—
да
Дата добавления (может быть null).
data.image_url
string
—
да
URL изображения товара (может быть null).
data.first_char
string
—
—
Первая буква названия (для группировки).
data.nds_percentage
string
—
да
Процент НДС (может быть null).
data.barcode
string
—
—
Штрихкод товара.
data.ntin
string
—
да
Код NTIN/маркировки (может быть null).
data.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.error_message
string
—
да
Текст ошибки (null если ошибок нет).
data.created_at
string
—
—
Дата и время создания.
data.synced_at
string
—
—
Дата последней синхронизации с Kaspi.
links
object
—
—
Ссылки пагинации.
meta
object
—
—
Метаданные пагинации.
POST /catalog
Создаёт один или несколько товаров в каталоге организации. Товары сохраняются локально со статусом pending и отправляются в Kaspi API через очередь.
Параметры запроса
Поле
Тип
Обяз.
Описание
items
object[]
Да
Массив товаров (макс. 50).
items.name
string
Да
Название товара.
items.selling_price
number
Да
Цена продажи.
items.unit_id
integer
Да
ID единицы измерения.
items.image_id
string
—
UUID изображения, полученный из upload-image.
items.barcode
string
—
Штрихкод товара.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
data
object[]
—
—
Массив записей результата.
data.id
integer
—
—
ID записи.
data.kaspi_item_id
string
—
да
ID товара в системе Kaspi (null до синхронизации).
data.name
string
—
—
Название.
data.unit_id
integer
—
—
ID единицы измерения.
data.selling_price
string
—
—
Цена продажи.
data.date_added
string
—
да
Дата добавления (может быть null).
data.image_url
string
—
да
URL изображения товара (может быть null).
data.first_char
string
—
—
Первая буква названия (для группировки).
data.nds_percentage
string
—
да
Процент НДС (может быть null).
data.barcode
string
—
—
Штрихкод товара.
data.ntin
string
—
да
Код NTIN/маркировки (может быть null).
data.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.error_message
string
—
да
Текст ошибки (null если ошибок нет).
data.created_at
string
—
—
Дата и время создания.
data.synced_at
string
—
—
Дата последней синхронизации с Kaspi.
POST /catalog/upload-image
Загружает и оптимизирует изображение товара. Использует MD5 дедупликацию — повторная загрузка того же изображения вернёт существующий image_id.
Параметры запроса
Поле
Тип
Обяз.
Описание
image
string
Да
Изображение товара (jpeg/png/webp, макс. 2 МБ).
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
image_id
string
—
—
UUID изображения.
PATCH /catalog/{id}
Обновляет данные товара каталога. Локальные поля обновляются сразу, изменения в Kaspi API отправляются через очередь.
Параметры запроса
Поле
Тип
Обяз.
Описание
name
string
—
Название товара.
selling_price
number
—
Цена продажи.
unit_id
integer
—
ID единицы измерения.
image_id
string
—
UUID нового изображения.
is_image_deleted
boolean
—
Удалить текущее изображение.
barcode
string
—
Штрихкод товара.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
catalog_item_id
integer
—
—
ID товара каталога.
DELETE /catalog/{id}
Помечает товар как удаляемый и отправляет запрос на удаление в Kaspi API через очередь.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
catalog_item_id
integer
—
—
ID товара каталога.
Подписки (Subscriptions)
GET /subscriptions
Возвращает пагинированный список подписок с возможностью фильтрации.
Параметры запроса (query)
Поле
Тип
Обяз.
Описание
status
string
—
Фильтр по статусу (active, paused, cancelled, expired).
external_subscriber_id
string
—
Фильтр по внешнему ID подписчика.
phone_number
string
—
Фильтр по номеру телефона (8XXXXXXXXXX).
per_page
integer
—
Кол-во на страницу (по умолчанию 20).
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
current_page
integer
—
—
Номер текущей страницы.
data
object[]
—
—
Массив записей результата.
data.id
integer
—
—
ID записи.
data.subscriber_name
string
—
—
Имя подписчика.
data.phone_number
string
—
—
Номер телефона подписчика.
data.external_subscriber_id
string
—
—
Внешний ID подписчика.
data.amount
string
—
—
Сумма в тенге.
data.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
data.description
string
—
—
Описание.
data.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
data.billing_period_label
string
—
—
Локализованное название периода биллинга.
data.billing_day
integer
—
—
День биллинга (1-28).
data.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
data.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.status_label
string
—
—
Локализованное название статуса.
data.status_color
string
—
—
Цвет статуса для UI.
data.started_at
string
—
—
Дата начала подписки.
data.next_billing_at
string
—
—
Дата следующего списания (null если нет).
data.next_billing_in_days
integer
—
—
Количество дней до следующего списания.
data.next_billing_label
string
—
—
Текстовое обозначение следующего списания.
data.paused_at
string
—
да
Дата приостановки (null если активна).
data.cancelled_at
string
—
да
Дата отмены (null если не отменена).
data.failed_attempts
integer
—
—
Количество неудачных попыток списания.
data.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
data.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
data.grace_period_days
integer
—
—
Льготный период в днях.
data.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
data.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
data.metadata
string
—
да
Произвольные метаданные.
data.created_at
string
—
—
Дата и время создания.
data.updated_at
string
—
—
Дата и время последнего обновления.
total
integer
—
—
Общее количество записей.
POST /subscriptions
Создаёт новую рекуррентную подписку. Первый счёт выставляется автоматически.
Параметры запроса
Поле
Тип
Обяз.
Описание
phone_number
string
Да
Номер телефона подписчика в формате 8XXXXXXXXXX.
billing_period
string
Да
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
billing_day
integer
—
День биллинга (1-28).
description
string
—
Описание подписки.
subscriber_name
string
—
Имя подписчика.
external_subscriber_id
string
—
Внешний ID подписчика в вашей системе.
started_at
string
—
Дата начала (Y-m-d). По умолчанию — сегодня.
max_retry_attempts
integer
—
Макс. кол-во повторных попыток при неудаче (1-10).
retry_interval_hours
integer
—
Интервал между повторами в часах (1-168).
grace_period_days
integer
—
Льготный период в днях (1-30).
metadata
object
—
Произвольные метаданные.
bill_immediately
boolean
—
Выставить первый счёт сразу при создании.
amount
number
—
Сумма списания в тенге (100-1000000). Обязательна для организаций без каталога.
cart_items
object[]
—
Корзина товаров (только для каталожных организаций).
cart_items.catalog_item_id
integer
Да
ID товара из каталога.
cart_items.count
integer
Да
Количество.
cart_items.price
number
—
Переопределённая цена за единицу. Если не указана — используется selling_price из каталога.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
—
Дата следующего списания (null если нет).
subscription.next_billing_in_days
integer
—
—
Количество дней до следующего списания.
subscription.next_billing_label
string
—
—
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
да
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
да
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
object
—
—
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
GET /subscriptions/{id}
Возвращает детальную информацию о подписке, включая статистику и последний платёж.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
—
Дата следующего списания (null если нет).
subscription.next_billing_in_days
integer
—
—
Количество дней до следующего списания.
subscription.next_billing_label
string
—
—
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
да
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
да
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
string
—
да
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
subscription.last_payment
object
—
—
Данные последнего платежа.
subscription.stats
object
—
—
Статистика по подписке.
PUT /subscriptions/{id}
Обновляет параметры существующей подписки. Все поля опциональны.
Параметры запроса
Поле
Тип
Обяз.
Описание
amount
number
—
Сумма списания в тенге (100-1000000).
billing_day
integer
—
День биллинга (1-28).
description
string
—
Описание подписки.
subscriber_name
string
—
Имя подписчика.
external_subscriber_id
string
—
Внешний ID подписчика.
max_retry_attempts
integer
—
Макс. кол-во повторных попыток (1-10).
retry_interval_hours
integer
—
Интервал между повторами в часах (1-168).
grace_period_days
integer
—
Льготный период в днях (1-30).
metadata
object
—
Произвольные метаданные.
cart_items
object[]
—
Корзина товаров (только для каталожных организаций).
cart_items.catalog_item_id
integer
Да
ID товара из каталога.
cart_items.count
integer
Да
Количество.
cart_items.price
number
—
Переопределённая цена за единицу. Если не указана — используется selling_price из каталога.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
—
Дата следующего списания (null если нет).
subscription.next_billing_in_days
integer
—
—
Количество дней до следующего списания.
subscription.next_billing_label
string
—
—
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
да
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
да
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
object
—
—
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
POST /subscriptions/{id}/pause
Приостанавливает активную подписку. Новые счета не будут выставляться до возобновления.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
да
Дата следующего списания (null если нет).
subscription.next_billing_in_days
string
—
да
Количество дней до следующего списания.
subscription.next_billing_label
string
—
да
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
—
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
да
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
string
—
да
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
POST /subscriptions/{id}/resume
Возобновляет приостановленную подписку. Следующий биллинг будет рассчитан автоматически.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
—
Дата следующего списания (null если нет).
subscription.next_billing_in_days
integer
—
—
Количество дней до следующего списания.
subscription.next_billing_label
string
—
—
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
да
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
да
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
string
—
да
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
POST /subscriptions/{id}/cancel
Безвозвратно отменяет подписку. Новые счета больше не будут выставляться.
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
message
string
—
—
Текстовое сообщение о результате операции.
subscription
object
—
—
Объект подписки.
subscription.id
integer
—
—
ID записи.
subscription.subscriber_name
string
—
—
Имя подписчика.
subscription.phone_number
string
—
—
Номер телефона подписчика.
subscription.external_subscriber_id
string
—
—
Внешний ID подписчика.
subscription.amount
string
—
—
Сумма в тенге.
subscription.cart_items
string
—
да
Корзина товаров подписки (null без каталога).
subscription.description
string
—
—
Описание.
subscription.billing_period
string
—
—
Период биллинга: daily, weekly, biweekly, monthly, quarterly, yearly.
subscription.billing_period_label
string
—
—
Локализованное название периода биллинга.
subscription.billing_day
integer
—
—
День биллинга (1-28).
subscription.billing_day_label
string
—
—
Локализованное обозначение дня биллинга.
subscription.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
subscription.status_label
string
—
—
Локализованное название статуса.
subscription.status_color
string
—
—
Цвет статуса для UI.
subscription.started_at
string
—
—
Дата начала подписки.
subscription.next_billing_at
string
—
да
Дата следующего списания (null если нет).
subscription.next_billing_in_days
string
—
да
Количество дней до следующего списания.
subscription.next_billing_label
string
—
да
Текстовое обозначение следующего списания.
subscription.paused_at
string
—
да
Дата приостановки (null если активна).
subscription.cancelled_at
string
—
—
Дата отмены (null если не отменена).
subscription.failed_attempts
integer
—
—
Количество неудачных попыток списания.
subscription.max_retry_attempts
integer
—
—
Макс. число повторных попыток.
subscription.retry_interval_hours
integer
—
—
Интервал между повторами в часах.
subscription.grace_period_days
integer
—
—
Льготный период в днях.
subscription.in_grace_period
boolean
—
—
Признак нахождения в льготном периоде.
subscription.is_sandbox
boolean
—
—
Признак тестового (sandbox) объекта.
subscription.metadata
string
—
да
Произвольные метаданные.
subscription.created_at
string
—
—
Дата и время создания.
subscription.updated_at
string
—
—
Дата и время последнего обновления.
GET /subscriptions/{id}/invoices
Возвращает пагинированный список всех платежей (счетов) по подписке.
Параметры запроса (query)
Поле
Тип
Обяз.
Описание
per_page
integer
—
Кол-во на страницу (по умолчанию 20).
Поля ответа
Поле
Тип
Обяз.
Nullable
Описание
data
object[]
—
—
Массив записей результата.
data.id
integer
—
—
ID записи.
data.invoice_id
integer
—
—
ID связанного счёта.
data.billing_period_start
string
—
—
Начало расчётного периода.
data.billing_period_end
string
—
—
Конец расчётного периода.
data.billing_period_label
string
—
—
Локализованное название периода биллинга.
data.amount
string
—
—
Сумма в тенге.
data.attempt_number
integer
—
—
Номер попытки списания.
data.status
string
—
—
Статус. Для счёта: pending, processing, cancelling, paid, cancelled, expired, error, partially_refunded. Для возврата: pending, processing, completed, failed. Для подписки: active, paused, cancelled, completed, expired. Для товара каталога: pending, active, error.
data.status_label
string
—
—
Локализованное название статуса.
data.status_color
string
—
—
Цвет статуса для UI.
data.paid_at
string
—
—
Дата и время оплаты (null если не оплачено).
data.failure_reason
string
—
да
Причина неудачи (null при успехе).
data.invoice
object
—
—
Связанный счёт.
data.created_at
string
—
—
Дата и время создания.
meta
object
—
—
Метаданные пагинации.
meta.current_page
integer
—
—
Номер текущей страницы.
meta.total
integer
—
—
Общее количество записей.
meta.per_page
integer
—
—
Количество записей на странице.
Webhooks
Webhooks настраиваются через личный кабинет ApiPay.kz (Настройки > Подключение). При создании webhook вы получите secret для верификации подписи (HMAC-SHA256).
События
invoice.status_changed
invoice.refunded
subscription.payment_succeeded
subscription.payment_failed
subscription.grace_period_started
subscription.expired
subscription.created
subscription.paused
subscription.resumed
subscription.cancelled
webhook.test
Когда приходит вебхук
Этот раздел перечисляет события, при которых ApiPay шлёт вебхук, и поясняет, в каком статусе он приходит. Технические статусы processing/cancelling вебхуков не порождают.
Событие
Статус
Когда
invoice.status_changed
pending
Счёт создан в Kaspi и ожидает оплату. Для счетов по номеру (POST /invoices) это первый вебхук после 201-ответа со status=processing. Для QR-счетов (POST /invoices/qr) pending-вебхук НЕ отправляется — статус возвращается синхронно в 201-ответе; первый вебхук по QR-счёту — оплата, отмена, истечение или ошибка.
invoice.status_changed
paid
Счёт оплачен. Может прийти и ПОСЛЕ cancelled/expired — оплата в последний момент выигрывает гонку (см. «Переходы статусов»).
invoice.status_changed
cancelled
Счёт отменён: вами через API, кассиром, либо автоматически — при создании нового QR той же кассой (error_message: «Заменён новым QR-счётом #N»).
invoice.status_changed
expired
Счёт истёк: 24 часа в Kaspi, либо ~5 минут для QR-токена.
invoice.status_changed
error
Техническая ошибка — счёт финализирован, система больше НЕ повторяет попытки по этому счёту. Причина — в error_code/error_message. Что делать — раздел «Сценарии реагирования».
invoice.status_changed
partially_refunded
Первый частичный возврат по счёту (дополнительно к invoice.refunded). Повторные частичные возвраты статус не меняют. Полный возврат статус НЕ меняет — счёт остаётся paid (или partially_refunded, если ранее был частичный) с is_fully_refunded=true.
invoice.refunded
completed
Возврат проведён. Включает возвраты, сделанные кассиром в приложении Kaspi (импортируются автоматически).
invoice.refunded
failed
Возврат не удался (refund.error_code). Система сама НЕ повторяет; сумма не блокируется — можно создать новый возврат.
subscription.created
—
Подписка создана через POST /subscriptions. Счета по подписке выставляет система автоматически: первый счёт будет выставлен в next_billing_at (или сразу при bill_immediately). По каждому счёту приходят обычные invoice-вебхуки.
subscription.payment_succeeded
—
Очередной счёт подписки оплачен. failed_attempts сброшен, льготный период (если был) снят.
subscription.payment_failed
—
Счёт подписки истёк или отменён (reason). Пока attempt_number < max_retry_attempts (по умолчанию 3) система САМА перевыставит счёт с интервалом retry_interval_hours (по умолчанию 24 ч) — ничего пересоздавать не нужно, просто уведомите клиента (attempt_number, reason). Счёт со статусом error провалом НЕ считается (это событие не придёт) — отслеживайте invoice-вебхук error.
subscription.grace_period_started
—
Все попытки исчерпаны; подписка ещё активна grace_period_days (по умолчанию 3) дней. Любая успешная оплата снимает льготный период.
subscription.expired
—
Льготный период истёк — биллинг остановлен навсегда, реактивации нет. Для возобновления создайте новую подписку.
subscription.paused
—
Подписка приостановлена (POST /subscriptions/{id}/pause). Счета не выставляются.
subscription.resumed
—
Подписка возобновлена; next_billing_at пересчитан от момента возобновления, пропущенные периоды не доначисляются.
subscription.cancelled
—
Подписка отменена безвозвратно (next_billing_at сохраняет последнее значение, счета не выставляются).
webhook.test
—
Ручной тест из ЛК (Настройки → API-ключи → Тест вебхука). Фиктивный счёт со status=test — receiver должен спокойно его игнорировать.
{
"event": "invoice.status_changed",
"invoice": {
"id": 44,
"external_order_id": null,
"amount": "15000.00",
"status": "cancelled",
"description": "QR на кассе",
"kaspi_invoice_id": "13234689514",
"client_name": null,
"client_phone": "87071234567",
"is_sandbox": false,
"cancelled_at": "2026-02-12T14:45:00+00:00",
"error_message": "Заменён новым QR-счётом #45"
},
"source": "My API Key",
"timestamp": "2026-02-12T14:45:01+00:00"
}
Поле
Тип
Nullable
Описание
event
string
—
Тип события — invoice.status_changed.
invoice.id
integer
—
Внутренний ID счёта в ApiPay.
invoice.external_order_id
string
да
Ваш внешний идентификатор заказа, переданный при создании счёта.
invoice.amount
string
—
Сумма счёта.
invoice.subtotal
string
да
Сумма до применения скидки. Только для счетов с корзиной/скидкой (subtotal и discount_sum приходят вместе).
invoice.discount_sum
string
да
Сумма скидки. Только для счетов с корзиной/скидкой (subtotal и discount_sum приходят вместе).
invoice.discount_percentage
string
да
Процент скидки. Только для счетов с корзиной/скидкой.
invoice.status
string
—
Статус счёта: pending / paid / cancelled / expired / error / partially_refunded. Статуса refunded не существует — полный возврат оставляет paid + is_fully_refunded=true.
invoice.kaspi_invoice_id
string
да
ID счёта в Kaspi. Появляется уже при pending (когда счёт создан в Kaspi); null — пока счёт не дошёл до Kaspi.
invoice.client_phone
string
—
Номер телефона клиента.
invoice.kaspi_source_type
string
да
Источник средств клиента: GOLD — дебетовая карта Kaspi, RED — кредитная карта Kaspi, LOAN — рассрочка/кредит, BUSINESSACCOUNT — бизнес-счёт, BANKINTEGRATIONACCOUNT — привязанный внешний банковский счёт. Обычно присутствует при status=paid, но гейтится по наличию значения, а не строго по статусу: поле приходит, когда Kaspi вернул значение (например, счёт, уже получивший его, может пробросить поле и в статусах cancelled/expired), и отсутствует/null иначе. Может отсутствовать для старых счетов; список может расширяться — обрабатывайте неизвестные значения как «прочее».
invoice.kaspi_sale_type
string
да
Способ приёма счёта: Remote — push на номер, QR — QR-код, Restaurant — ресторанный счёт, Static — статичный QR. Обычно присутствует при status=paid, но гейтится по наличию значения, а не строго по статусу: поле приходит, когда Kaspi вернул значение (например, счёт, уже получивший его, может пробросить поле и в статусах cancelled/expired), и отсутствует/null иначе. Может отсутствовать для старых счетов; список может расширяться — обрабатывайте неизвестные значения как «прочее».
invoice.paid_at
string
да
Время оплаты счёта (ISO 8601). Поле отсутствует во всех статусах, кроме paid (а не null до оплаты).
invoice.error_message
string
да
Человекочитаемая причина. При status=error присутствует всегда; при status=cancelled — только если заполнена (например "Заменён новым QR-счётом #N" при замене QR). В статусах paid/pending/expired поле отсутствует.
invoice.error_code
string
да
Стабильный snake_case-код из каталога (раздел "Коды ошибок"). Присутствует только если не null и только при status=error/cancelled. Стройте switch-логику по нему, а не по тексту.
invoice.cancelled_at
string
да
Время перехода в cancelled (ISO 8601). Присутствует только при соответствующем статусе.
invoice.expired_at
string
да
Время перехода в expired (ISO 8601). Присутствует только при соответствующем статусе.
invoice.errored_at
string
да
Время перехода в error (ISO 8601). Присутствует только при соответствующем статусе.
pending / processing / completed / failed. Вебхук приходит на completed И на failed.
refund.kaspi_refund_id
string
да
ID возврата в Kaspi; null при неудаче.
refund.reason
string
да
Причина возврата.
refund.created_at
string
—
Время создания возврата (ISO 8601).
refund.error_code
string
да
Только при status=failed. Например refund_window_expired — истёк срок возврата (~14 дней). Поля error_message в вебхуке нет by design — текст смотрите в GET /invoices/{id}/refunds или резолвите код по каталогу.
refund.items
array
да
Позиции возврата (только для позиционных возвратов): catalog_item_id, name, price, count, amount.
invoice.id
integer
—
Внутренний ID счёта в ApiPay.
invoice.external_order_id
string
да
Ваш внешний идентификатор заказа.
invoice.amount
string
—
Сумма счёта.
invoice.subtotal
string
—
Сумма счёта до применения скидки.
invoice.discount_sum
string
—
Сумма скидки по счёту.
invoice.total_refunded
string
—
Суммарно возвращено по счёту на текущий момент.
invoice.available_for_refund
number
—
Сумма, ещё доступная для возврата. Приходит числом (float), в отличие от amount и total_refunded, которые передаются строками.
invoice.is_fully_refunded
boolean
—
true, если счёт возвращён полностью.
invoice.is_sandbox
boolean
—
Счёт создан в sandbox-режиме.
invoice.status
string
—
Статус счёта после возврата. Полный возврат статус НЕ меняет (остаётся paid — или partially_refunded, если ранее был частичный) + is_fully_refunded=true; первый частичный переводит в partially_refunded (и дополнительно приходит invoice.status_changed).
Дата следующего списания (ISO 8601). НЕ обнуляется при отмене — сохраняет последнее значение; счета больше не выставляются.
subscription.failed_attempts
integer
—
Количество подряд неуспешных попыток списания.
subscription.in_grace_period
boolean
—
Находится ли подписка в льготном периоде.
subscription.is_sandbox
boolean
—
Подписка создана в sandbox-режиме.
source
string
да
Название API-ключа.
timestamp
string
—
Время отправки события (ISO 8601).
Переходы статусов
Гарантия: ровно один вебхук на реальный переход статуса. Дубли подряд одного статуса, технические processing/cancelling, «протухший» pending после терминального статуса и error после paid — подавляются. При этом повторная доставка одного и того же перехода возможна (ретраи после частичной доставки) — дедуплицируйте по (invoice.id, status) и (refund.id, status). Отвечайте 200 OK быстро (≤5 секунд), обрабатывайте асинхронно.
cancelled | expired → paid — оплата в последний момент выигрывает гонку — обработайте как «деньги получены»: отгрузите или сделайте возврат. Это не баг.
error → pending — реконсиляция: счёт на самом деле успел создаться в Kaspi — следуйте последнему статусу
paid → partially_refunded — первый частичный возврат
Никогда не происходят
терминальный → pending — кроме error → pending (реконсиляция)
paid → error — подавляется как инцидент
error → paid — невозможен
Без вебхука
202-ответ на отмену переводит счёт в cancelling БЕЗ вебхука; если Kaspi отказал в отмене (обычно счёт уже оплачен) — счёт тихо возвращается в pending, реальный статус (обычно paid) доставит синхронизация в течение минут. После 202 не считайте счёт отменённым — ждите вебхук.
Сценарии реагирования
Когда вебхук приносит status=error (счёт) или status=failed (возврат) — операция финализирована: система уже исчерпала собственные ретраи и больше НЕ будет повторять её сама. «Повторить» всегда означает «создать новую операцию». Пока счёт в processing — система ретраит сама, вмешиваться не нужно (легитимный бэклог может держать счёт в processing больше часа при троттлинге Kaspi).
Ошибка
Что произошло
Что делает система
Что делать вам
client_not_found
Номер телефона не зарегистрирован в Kaspi
Финализирует счёт сразу, без ретраев
Запросите у клиента другой номер и создайте новый счёт
network_unavailable
Сеть/Kaspi были недоступны
Ретраила сама; вебхук означает, что ретраи исчерпаны
Создайте новый счёт/возврат через 1–2 минуты
session_transient
Временный сбой сессии кассира
Автоматически инвалидировала сессию и ретраила
Создайте новый счёт позже; если повторяется — переподключите кассира в ЛК
kaspi_throttled
Kaspi ограничил частоту запросов кассы
Автоматически замедляет очередь этой кассы (интервал до 3 минут на счёт) и ретраит; вебхук = финализация
Пока счёт в processing — ничего. После error — новый счёт через 2–3 минуты; снизьте темп создания счетов
organization_not_configured
К организации не подключён кассир Kaspi
Финализирует сразу
Подключите кассира: ЛК → Настройки → Авторизация Kaspi
invoice_already_paid
Попытка отменить уже оплаченный счёт
Отмену остановила; деньги получены
Не отменяйте; если нужно вернуть деньги — создайте возврат
invoice_already_cancelled
Счёт уже отменён
—
Ничего: желаемое состояние уже достигнуто
invoice_not_found_in_kaspi
Kaspi не нашёл счёт при отмене
Финализирует error
Обратитесь в поддержку
refund_window_expired
Истёк срок возврата (~14 дней) или возврат уже сделан
Возврат failed, ретраев нет
Не повторяйте; сообщите клиенту или обратитесь в поддержку
qr_render_failed
Не сформировалось изображение QR
Счёт финализирован в error (и 500-ответ, и вебхук)
Повторите POST /invoices/qr — создастся новый счёт
kaspi_session_invalid
Сессия кассира истекла в момент создания QR
Счёт финализирован в error; сессия инвалидирована
Повторите позже; если повторяется — переподключите кассира
kaspi_error
Неклассифицированная ошибка Kaspi
Зависит от причины; для QR — счёт error + вебхук
Читайте message/error_message; повторите или обратитесь в поддержку
unknown_error
Непредвиденная ошибка (в т.ч. исчерпаны все попытки создания)
Финализировала после всех ретраев
Создайте новый счёт; если повторяется — поддержка
Без error_code
QR заменён новым (supersede) — Вебхук cancelled с error_message «Заменён новым QR-счётом #N» (без error_code). Kaspi держит один активный QR на кассу — это штатно. Используйте новый QR.
Счёт истёк — Вебхук expired (24 ч, QR ~5 мин). При необходимости создайте новый счёт.
Оплата после отмены/истечения — Вебхук paid после cancelled/expired: деньги получены — отгрузите или сделайте возврат.
Возврат к pending после error — Корректирующий pending-вебхук (реконсиляция). Следуйте последнему статусу.
Retry Policy
Subscription webhooks — До 11 попыток: первая доставка + 10 повторов при неуспехе. Интервалы нарастают: 10с, 30с, 1м, 1.5м, 2м, 5м, 10м, 15м, 30м, 1ч (всего ~2 часа)
Invoice webhooks — До 11 попыток: первая доставка + 10 повторов при неуспехе. Интервалы нарастают: 10с, 30с, 1м, 1.5м, 2м, 5м, 10м, 15м, 30м, 1ч (всего ~2 часа)
Timeout: 5 секунд на ответ (плюс до 3 секунд на установление соединения)
HTTP 2xx = success
Sandbox: В sandbox-режиме invoice-вебхуки доставляются всего за 3 попытки (интервалы 5с, 15с). Вебхуки refund и subscription всегда используют полные 11 попыток — sandbox-сокращения для них нет.
3xx/4xx: Ретраится только HTTP ≥500, ровно 429 и сетевые ошибки. Ответы 3xx и 4xx (кроме 429) НЕ ретраятся — попытка сразу фиксируется как доставленная. Повторить такую доставку можно только вручную: ЛК → Webhook-логи → Retry (доступно для записей со status=failed, cooldown между ручными повторами — 10 секунд).
Дедупликация: Дедупликация на стороне клиента обязательна: ретрай после частичной доставки двум получателям пере-отправляет вебхук всем. Ключи дедупликации: (invoice.id, invoice.status) для invoice-событий, (refund.id, refund.status) для возвратов, (event, subscription.id, invoice_id) для событий подписки. Отвечайте 200 OK быстро (≤5 секунд), обрабатывайте асинхронно.
Наблюдаемость подписок: События subscription.* не пишутся в Webhook-логи ЛК: для них нет ручного retry и circuit breaker (известное ограничение).
UTC: Все даты в вебхуках — ISO 8601 в UTC (+00:00).
Circuit breaker
Если ваш endpoint стабильно недоступен, отправка на ключ приостанавливается: 5 подряд неудач → пауза 5 минут, 10 → 30 минут, 20 → 2 часа, 50 → полное отключение до ручного вмешательства. Вебхуки за время паузы НЕ доотправляются — сверяйте состояние через GET-методы. Любая успешная доставка (или успешный тест-вебхук из ЛК) сбрасывает счётчик. Статус виден в списке API-ключей.
Ошибки приходят в нескольких формах — не путайте их между собой:
HTTP-статус (400, 401, 429 …) — общий класс ошибки, присутствует всегда.
Поле error в теле ответа — конкретная причина синхронной ошибки. Историческая особенность бэкенда: часть значений — машинные коды в snake_case (organization_required, kaspi_session_not_configured), а часть — английские фразы целиком (Organization not found or not verified, Invoice cannot be cancelled). Поэтому форматы и различаются. Не сравнивайте текст error в коде.
Поле error_message — человекочитаемый текст асинхронной ошибки Kaspi (счёт создан со статусом processing и позже перешёл в error). Фиксированных кодов у Kaspi нет.
Поле error_code (новое) — стабильный snake_case-код из фиксированного каталога. Стройте switch-логику по нему, а не по тексту error/error_message.
В колонке «Код» ниже значения сгруппированы по тому, где именно они появляются.
HTTP-статусы (общие для всех эндпоинтов)
Код
Описание
400
Bad Request — некорректный запрос или недопустимое состояние. Точная причина — в поле message или error
401
Unauthorized — API-ключ отсутствует, неверен, истёк или не привязан к организации; либо аккаунт деактивирован
403
Forbidden — организация заморожена (suspended) или не верифицирована для рабочего режима
404
Not Found — ресурс не найден или принадлежит другой организации
422
Validation Error — ошибка валидации полей; детали в объекте errors
429
Too Many Requests — превышен общий лимит Public API (200 запросов/мин на API-ключ); смотрите заголовок Retry-After. Отдельно: POST /clients/check ограничен 60 запросами/мин и 10 000 запросами/сутки на API-ключ, а POST /invoices/qr — 60 QR/мин на организацию (на этом 429 заголовка Retry-After нет)
500
Server Error — внутренняя ошибка сервера
502
Bad Gateway — ошибка на стороне Kaspi API
503
Service Unavailable — сессия Kaspi недействительна или истекла
Поле «error» при создании счёта (POST /invoices, /invoices/qr)
Код
Описание
organization_required (400)
Организация не подключена — создайте sandbox-организацию для тестов или подключите кассира Kaspi
Organization not found or not verified (400)
Рабочий режим: организация не верифицирована. Дождитесь верификации или тестируйте в песочнице
kaspi_session_not_configured (400)
Кассир Kaspi не подключён. Подключите его в кабинете (Настройки → Авторизация Kaspi) или через поддержку (WhatsApp +7 708 516 74 89)
kaspi_session_invalid (503)
Сессия кассира Kaspi истекла или сброшена. Переподключите кассира — запросите новый SMS-код
connection_ambiguous (422)
У организации несколько активных касс, основная не выбрана — передайте kaspi_connection_id
sandbox_invoice_limit (400)
Достигнут лимит тестовых счетов (500 на организацию) — очистите песочницу в кабинете
Поле «error» только для QR-счёта (POST /invoices/qr)
Код
Описание
qr_rate_limit (429)
Слишком много QR-запросов для организации (лимит 60/мин) — подождите минуту. Заголовок Retry-After на этом 429 не возвращается (в отличие от общего лимита Public API 200/мин)
qr_render_failed (500)
Не удалось сформировать изображение QR-кода — повторите запрос позже
kaspi_error (502)
Kaspi API вернул ошибку при создании QR-токена — повторите позже
Поле «error» при отмене и возврате
Код
Описание
Invoice cannot be cancelled (400)
Отменить можно только счёт в статусе pending или processing
Invoice is not refundable (400)
Возврат возможен только по оплаченному счёту, ещё не возвращённому полностью
Refund amount exceeds available amount (400)
Сумма возврата больше доступной — смотрите available_for_refund в GET /invoices/{id}
Поле «error» при работе с подписками
Код
Описание
sandbox_subscription_limit (400)
Достигнут лимит тестовых подписок (10 на организацию) — очистите песочницу
Organization not verified (403)
Подписки в рабочем режиме доступны только верифицированной организации
Асинхронные ошибки Kaspi: status=error / поле error_message (HTTP-кода нет)
Код
Описание
status=error
Счёт создан (201, статус processing), но Kaspi не смог его обработать — статус сменился на error. Причина текстом в поле error_message (GET /invoices/{id}). У Kaspi нет фиксированных кодов — текст приходит как есть
error_message: номер не в Kaspi
«Этот номер телефона не зарегистрирован в Kaspi. Укажите номер с установленным приложением Kaspi.» — у клиента нет приложения Kaspi; попросите другой номер
error_message: сбой Kaspi
«Ошибка обработки платежа. Обратитесь в поддержку» или «Не удалось обработать счёт после нескольких попыток» — временный сбой Kaspi; повторите создание счёта позже
Поле error_code — стабильный машинный код (новое, рекомендуется)
Код
Описание
error_code (новое поле)
Стабильный snake_case-код ошибки из фиксированного каталога (21 значение). Присутствует в JSON-ответах об ошибках и в webhook-объектах invoice (для status=error) и refund (для status=failed). Поля message/error сохранены без изменений. Определяйте тип ошибки по error_code, текст — для показа пользователю. У каждого кода ниже указана «Доставка» — приходит ли он асинхронно (в webhook) или синхронно (HTTP-ответ с кодом)
error_code: network_unavailable
Сервис временно недоступен (сбой сети/Kaspi). Можно повторить позже. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: session_transient
Временные проблемы авторизации Kaspi. Можно повторить попытку. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: client_not_found
Номер телефона не зарегистрирован в Kaspi. Не повторяемая — попросите другой номер. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: kaspi_throttled
Kaspi ограничил частоту запросов. Повторите через 2–3 минуты. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: refund_window_expired
Срок возврата истёк или возврат уже сделан (часто для refund status=failed). Доставка: async — приходит в invoice.refunded (refund.error_code)
error_code: invoice_already_paid
Счёт уже оплачен. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: invoice_already_cancelled
Счёт уже отменён. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: invoice_not_found_in_kaspi
Счёт не найден в Kaspi. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: organization_not_configured
Организация не настроена (нет рабочей привязки Kaspi). Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: unknown_error
Непредвиденная ошибка обработки — обратитесь в поддержку. Доставка: async — приходит в invoice.status_changed (invoice.error_code)
error_code: qr_render_failed
Не удалось сформировать изображение QR-кода — повторите запрос. Доставка: sync HTTP 500 + async (webhook invoice.status_changed, status=error)
error_code: kaspi_session_invalid
Сессия кассира Kaspi истекла или сброшена — переподключите кассира. Доставка: sync HTTP 503 + async (webhook invoice.status_changed, status=error)
error_code: kaspi_session_unavailable
Не удалось проверить сессию Kaspi — попробуйте позже. Доставка: sync HTTP 503
error_code: manager_throttled
Слишком много операций — попробуйте позже. Доставка: sync HTTP 429
Не удалось отправить код WhatsApp — попробуйте ещё раз. Доставка: sync HTTP 500
error_code: subscription_payment_failed
Не удалось создать счёт для оплаты подписки — попробуйте позже. Доставка: sync HTTP 502
error_code: kaspi_error
Kaspi API вернул ошибку — повторите позже. Текст message содержит конкретную причину от Kaspi. Доставка: sync HTTP 502 + async (для QR-счетов: webhook invoice.status_changed, status=error)
error_code: cart_items_mismatch
Зарезервирован — сейчас не используется бэкендом (такие случаи приходят как kaspi_error). Доставка: —
error_code: image_upload_failed
Зарезервирован — сейчас не используется бэкендом. Доставка: —
error_code: catalog_item_not_found
Зарезервирован — сейчас не используется бэкендом. Доставка: —
2026-06-11 [new] Вебхуки: добавлены уведомления о неудачных возвратах (invoice.refunded со status=failed и refund.error_code), об ошибках QR-счетов (invoice.status_changed со status=error и error_code: kaspi_session_invalid / kaspi_error / qr_render_failed — дублирует синхронный 5xx-ответ) и о первом частичном возврате (invoice.status_changed со status=partially_refunded). Изменения аддитивные, HMAC-подпись не менялась.
2026-06-11 [new] Документация вебхуков актуализирована: добавлены события subscription.created/paused/resumed/cancelled, примеры payload для статусов error/cancelled/failed, разделы «Когда приходит вебхук», «Переходы статусов» (включая легитимные cancelled→paid и error→pending) и «Сценарии реагирования» (что система делает автоматически и что делать интегратору по каждому error_code), описание circuit breaker и ретраев (включая sandbox). Исправлено: даты в вебхуках — UTC; статуса refunded у счёта не существует (полный возврат оставляет paid + is_fully_refunded).
2026-06-01 [new] GET /catalog: добавлен опциональный query-параметр statuses[] — фильтр списка товаров по статусу (можно передать несколько значений: active, pending, deleting, failed). Фильтрация выполняется на сервере до пагинации, поэтому meta.total и last_page отражают отфильтрованную выборку. Изменение аддитивное: без параметра поведение прежнее (возвращаются все видимые товары). Статус deleted в выдачу не попадает by design.
2026-05-29 [changed] Уточнения в документации по итогам сверки с бэкендом: общий лимит Public API — 200 запросов/мин на API-ключ (при 429 — заголовок Retry-After); лимиты 60 запросов/мин и 10 000 запросов/сутки относятся только к POST /clients/check; POST /invoices/qr — отдельный счётчик 60 QR/мин на организацию (на его 429 заголовка Retry-After нет). Лимит тестовых счетов в sandbox — 500 на организацию. В каталоге error_code для каждого кода размечена синхронность доставки: async (приходит в webhook) либо sync (HTTP-ответ с указанным кодом).
2026-05-28 [new] Унификация ошибок: во все JSON-ответы об ошибках и в webhook-объекты invoice (status=error) и refund (status=failed) добавлено новое поле error_code — стабильный snake_case-код из фиксированного каталога (например client_not_found, network_unavailable, kaspi_error, qr_render_failed, kaspi_session_invalid). Изменение аддитивное и обратно совместимое: поля message и error не изменились. Определяйте тип ошибки по error_code, а не по тексту message. error_code в refund-объекте приходит без error_message; HMAC-подпись webhook не менялась.
2026-05-28 [new] Webhook invoice.status_changed теперь может содержать два опциональных поля от Kaspi: kaspi_source_type — источник средств клиента (GOLD — дебетовая карта Kaspi, RED — кредитная карта Kaspi, LOAN — рассрочка/кредит, BUSINESSACCOUNT — бизнес-счёт, BANKINTEGRATIONACCOUNT — привязанный внешний банковский счёт) и kaspi_sale_type — способ приёма счёта (Remote — push на номер, QR — QR-код, Restaurant — ресторанный счёт, Static — статичный QR). Обычно приходят при status=paid, но гейтятся по наличию значения, а не строго по статусу: присутствуют, когда Kaspi вернул значение (счёт, уже получивший его, может пробросить поле и в cancelled/expired), и отсутствуют/null иначе — обрабатывайте как nullable. Перечислены известные на сегодня значения; список может расширяться на стороне Kaspi, поэтому обрабатывайте неизвестные значения как «прочее».
2026-05-28 [changed] Уточнение по POST /invoices/qr: лимит «один активный QR» считается per-кассир (kaspi_connection_id), а не на всю организацию. Если у организации несколько активных касс, можно показывать разные QR одновременно — на каждую кассу свой. В теле запроса добавлен опциональный kaspi_connection_id — для multi-cashier-организаций (если не указан и >1 активной кассы без primary, вернётся 422 connection_ambiguous). Параллельные запросы на одну и ту же кассу больше не блокируются — race ловится conditional UPDATE; проигравший запрос получает 409 superseded с invoice_id уже отменённого счёта, по этому id придёт webhook со status=cancelled. Дедуплицируйте по (invoice_id + status). В sandbox-режиме scope — per-organization (одна касса). Phone-инвойсы (is_qr_token=false) — TTL 24 часа в Kaspi, не затрагиваются supersede-логикой.
2026-05-27 [new] Новый эндпоинт POST /api/v1/clients/check — точечная проверка номера на наличие в Kaspi перед созданием счёта или подписки. Возвращает { phone (нормализованный), has_kaspi, client_name }. Нормализует форматы +7/7/8 и убирает пробелы/дефисы. Не предназначен для массового перебора номеров — на стороне сервера работает детекция аномалий, при срабатывании API-ключ деактивируется без предупреждения, организация блокируется до ручной проверки.
2026-05-09 [new] Новый эндпоинт POST /api/v1/invoices/qr — оплата по QR-коду на экране кассы без номера телефона. В ответе qr_token_url (Kaspi-ссылка) и qr_image_url (PNG 600×600 с логотипом Kaspi). TTL — 5 минут. После оплаты прилетает обычный webhook invoice.status_changed (status: paid). Поддерживаются режимы с каталогом (cart_items, discount_percentage) и без (amount). Для sandbox-организаций добавлен опциональный параметр simulate (paid|cancelled|expired) — создаёт счёт сразу в нужном терминальном статусе с мгновенной отправкой webhook (отдельный вызов /simulate-status больше не нужен, но по-прежнему работает).
2026-05-01 [changed] Параметр price в cart_items — переопределение цены позиции (если не указан, берётся selling_price из каталога)
2026-04-07 [new] Статус processing — счёт создан и ожидает отправки в Kaspi
2026-04-07 [new] Статус error и поле error_message — описание ошибки при сбое отправки
2026-04-07 [new] Параметр discount_percentage — скидка на весь чек (1-99%)
2026-04-07 [new] Параметр bill_immediately — выставить первый счёт подписки сразу
2026-04-07 [changed] POST /invoices/{id}/cancel — теперь работает для статусов pending и processing
2026-04-02 [changed] Обновлена API спецификация: удалён GET /status, добавлены новые поля в каталоге (kaspi_item_id, nds_percentage, ntin, synced_at и др.), подписках (billing_period_label, status_label, status_color, paused_at, cancelled_at и др.) и возвратах (organization_id, updated_at), обновлён формат пагинации
2026-03-29 [new] Добавлен параметр bill_immediately в POST /subscriptions — немедленное выставление первого счёта при создании подписки
2026-03-29 [new] Добавлено поле created_at в ответы эндпоинтов каталога (GET /catalog, POST /catalog) — дата создания товара в системе в формате ISO 8601
2026-03-27 [new] Запуск публичной API документации с единым источником правды