API v1 (/v1/*) объявлен deprecated и будет отключён 31 декабря 2026 года; на замену пришёл v2 (/v2/*) с обязательной идемпотентностью и чистыми JSON-ответами вместо legacy-обёртки. Чтобы мигрировать, замените пути по таблице ниже, начните слать заголовок Idempotency-Key на всех мутирующих POST и перестаньте разбирать конверт {StatusCode, Data, Message} — v2 отдаёт данные напрямую. Авторизация не меняется: тот же заголовок X-API-Key.
Каждый ответ v1 уже несёт предупреждающие заголовки Deprecation: true, Sunset: Wed, 31 Dec 2026 23:59:59 GMT и Link: . Полный справочник новых путей — в API Reference, детали по QR и счетам — в QR и счета.
Таблица соответствий путей v1 → v2
Каждому пути v1 соответствует преемник в v2. Один путь (/v1/status) прямого аналога не имеет.
| Метод | v1 (deprecated) | Метод | v2 (актуальный) |
|---|---|---|---|
| POST | /v1/qr/create | POST | /v2/qr |
| GET | /v1/qr/status/{op_id} | GET | /v2/qr/{op_id} |
| POST | /v1/invoice/create | POST | /v2/invoices |
| POST | /v1/invoice/cancel | POST | /v2/invoices/{op_id}/cancel |
| POST | /v1/history | GET | /v2/payments |
| POST | /v1/refund | POST | /v2/refunds |
| GET | /v1/client/{phone} | GET | /v2/client?phone= |
| GET | /v1/status | — | нет прямого аналога (см. ниже) |
Обратите внимание: /v1/history был POST, а его преемник GET /v2/payments — обычный GET с query-параметрами и курсорной пагинацией. /v1/client/{phone} переехал с path-параметра на query ?phone=.
Что нового в v2
Ключевые отличия, которые нужно учесть при переносе кода.
- •Idempotency-Key обязателен на
POST /v2/qr,POST /v2/invoices,POST /v2/invoices/{op_id}/cancel,POST /v2/refunds. Без него —400 idempotency_key_required. Генерируйте один ключ на намерение пользователя. Подробно — Идемпотентность. - •Чистые JSON-ответы вместо legacy-обёртки
{StatusCode, Data, Message}.POST /v2/qrсразу возвращает{operation_id, qr_token, deep_link, expires_at, status}. - •Единый формат ошибок: объект
{error: {type, code, message, request_id, param, category}}вместоMessage. Ветвление поcategory— см. Ошибки. - •Курсорная пагинация в списках: конверт
{data[], next_cursor, has_more}вместо плоского массива. См. Пагинация. - •Мультиаккаунт Kaspi: заголовок
X-Kaspi-Account:для выбора аккаунта при создании платежа.
Что делать с /v1/status
GET /v1/status возвращал {session_alive, usage_today, usage_month, rate_limit_per_minute} и прямого аналога в v2 не имеет. В кабинете те же данные доступны через JWT: живость сессии — в поле session_alive ответа GET /me, счётчики использования — в GET /me/usage. Если интеграция опиралась на /v1/status только ради проверки жив ли Kaspi-аккаунт, перенесите её на GET /me или следите за ошибкой 503 session_expired от рабочих вызовов.
curl: до и после
Создание QR на v1 (legacy-обёртка в ответе):
curl -X POST https://api.paybot.kz/v1/qr/create \
-H "X-API-Key: kp_live_0123456789abcdef0123456789abcdef" \
-H "Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff" \
-H "Content-Type: application/json" \
-d '{"amount": 5000}'{
"StatusCode": 0,
"Data": { "operation_id": "op_3fa8", "qr_token": "kzqr_9d8f" },
"Message": "OK"
}То же самое на v2 (данные напрямую, без обёртки):
curl -X POST https://api.paybot.kz/v2/qr \
-H "X-API-Key: kp_live_0123456789abcdef0123456789abcdef" \
-H "Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff" \
-H "Content-Type: application/json" \
-d '{"amount": 5000, "comment": "Заказ #1042"}'{
"operation_id": "op_3fa85f6457174562",
"qr_token": "kzqr_9d8f7a6b5c4d",
"deep_link": "https://kaspi.kz/pay/9d8f7a6b5c4d",
"expires_at": "2026-07-16T12:05:00Z",
"status": "created"
}Как изменился формат ответа
В v1 успех и данные заворачивались в конверт {StatusCode, Data, Message}, а ошибка передавалась текстом в Message. В v2 данные приходят объектом верхнего уровня, а любая ошибка — единым объектом {error: {type, code, message, request_id, param, category}}.
- •Уберите распаковку
Data— читайте поля прямо из корня ответа (response.operation_id, а неresponse.Data.operation_id). - •Перестаньте парсить
Messageдля определения ошибки — проверяйте HTTP-статус и, при ошибке, ветвитесь поerror.category(auth | validation | rate_limit | temporary | permanent). - •Сохраняйте
error.request_idв логах — по нему поддержка находит запрос. - •Поле
error.paramподсказывает конкретное поле ввода приvalidation— подсвечивайте именно его.
Полная таблица кодов и правило показа по category — в Ошибки.
Как изменились списки
Списочные эндпоинты v2 отдают курсорную пагинацию вместо плоского массива. POST /v1/history возвращал массив записей; преемник GET /v2/payments возвращает конверт {data[], next_cursor, has_more}.
- •Признак «есть ещё страница» — поле
has_more, а не длина массива. - •
next_cursor— opaque-строка: передавайте её как есть в параметрcursorследующего запроса, не парсите и не собирайте вручную. - •Нумерованных страниц и
totalнет — стройте «Загрузить ещё» или бесконечный скролл.
Детали пагинации и лимиты limit — в Пагинация.
Рекомендация по срокам
Мигрируйте на v2 сейчас, не дожидаясь sunset. v1 продолжает работать до 31 декабря 2026 года, но новых возможностей (платёжные ссылки, hosted checkout, единый формат ошибок и событий) в нём нет и не будет. После даты sunset вызовы /v1/* перестанут отвечать. Проверяйте заголовок Deprecation: true в логах, чтобы найти код, всё ещё зовущий старые пути.
FAQ
Изменится ли мой API-ключ при переходе на v2?
Нет. Тот же ключ kp_live_... работает и на /v1/*, и на /v2/* — авторизация идентична (X-API-Key). Перевыпускать ключ для миграции не нужно.
Что вернёт v1 после даты sunset?
После 31 декабря 2026 пути /v1/* выводятся из эксплуатации и перестают обслуживать запросы. До этой даты они работают, но помечены заголовком Deprecation: true. Планируйте перенос заранее — полный список преемников есть в API Reference.
Нужно ли переписывать обработку ответов?
Да, в части структуры. v1 заворачивал данные в {StatusCode, Data, Message}, а v2 отдаёт объект напрямую и использует единый формат ошибок {error: {...}}. Уберите распаковку Data и перенесите ветвление ошибок на поле category — см. Ошибки.
Что делать, если один и тот же ключ идёт на v1 и v2?
Так делать можно на переходный период: авторизация общая. Но помните, что мутирующие v1-вызовы для enterprise-плана раньше генерировали Idempotency-Key автоматически, а v2 требует его всегда и от всех планов. Приведите клиентский код к единому правилу — «один ключ на намерение пользователя» для обеих версий, чтобы поведение не расходилось.
Как проверить, что миграция завершена?
Убедитесь, что в логах интеграции больше нет ответов с заголовком Deprecation: true — он присутствует только на путях /v1/*. Отсутствие этого заголовка на протяжении полного цикла нагрузки означает, что старые пути больше не вызываются и можно спокойно дожидаться sunset.