# Kaspi POS Automation API > Self-hosted multi-tenant REST gateway for Kaspi Pay. Drop-in compatible > with apipay.kz: same `X-API-Key` auth, same error envelope, same > invoice status vocabulary, same `invoice.status_changed` webhook. > Use this API to take phone-based or QR Kaspi Pay payments, manage a > catalog, and receive signed webhooks when payment status changes. ## Documentation - [API reference (RU)](docs/API.md) — full `/api/v1/*` reference with endpoints, error envelope, status table, webhook spec. - [API reference (KK)](docs/API.kk.md) — Kazakh translation of the API reference. - [OpenAPI 3.0 spec](docs/openapi.yaml) — machine-readable schema for code generation. - [CRYPTO design notes](docs/CRYPTO.md) — RSA/ECDH/AES-GCM invariants for the Kaspi wire contract. - [README (RU)](README.md) and [README (KK)](README.kk.md) — project overview and quickstart. - [CHANGELOG](CHANGELOG.md) — release notes. ## API Base URL: `https:///api/v1` (dev: `http://localhost:3000/api/v1`). Invoices: - `POST /invoices` — Create a phone-based invoice. - `POST /invoices/qr` — Create a QR invoice. - `GET /invoices` — List invoices (filters: `status`, `from`, `to`, `limit`, `offset`). - `GET /invoices/{id}` — Invoice detail. - `POST /invoices/{id}/cancel` — Cancel a `pending` invoice. - `POST /invoices/{id}/refund` — Full or partial refund. - `GET /invoices/{id}/refunds` — Refunds for an invoice. - `POST /invoices/status/check` — Bulk status verification. Refunds: - `GET /refunds` — All refunds across the tenant. Catalog: - `GET /catalog/units` — Measurement units. - `GET /catalog` — List catalog items. - `POST /catalog` — Batch create catalog items. - `POST /catalog/upload-image` — Upload a product image (`multipart/form-data`, field `file`, up to 5 MB, MIME `image/jpeg|png|webp`). - `GET /catalog/images/{imageId}` — Serve a stored image (tenant-scoped). - `GET /catalog/{id}` / `PATCH /catalog/{id}` / `DELETE /catalog/{id}` — Item CRUD. Webhooks (configuration): - `GET /webhook/configure` — Read the current webhook config. - `POST /webhook/configure` — Set URL / enabled / events. - `POST /webhook/test` — Send a `webhook.test` event. Account / system: - `GET /me` — Current tenant + API key. - `GET /health` — Liveness (unauthenticated). ## Authentication Every `/api/v1/*` request except `/health` requires: ``` X-API-Key: ``` - Comparison is timing-safe. - Keys prefixed `kpa_test_*` carry `is_sandbox: true` (sandbox routing). - Keys prefixed `kpa_live_*` route through Kaspi when a session is connected. Rate limit: 60 requests per minute per tenant. Exceeded requests return HTTP 429 with `Retry-After` (seconds) and the apipay error envelope. Error envelope: ```json { "error": { "code": "VALIDATION_ERROR", "message": "...", "fields": {} } } ``` ## Webhooks Single canonical event: `invoice.status_changed`. Configure URL + events via `POST /webhook/configure`. Headers on every outbound webhook POST: ``` Content-Type: application/json X-Webhook-Signature: sha256= ``` Signature = `HMAC-SHA256(tenant.webhook.secret, raw_body_bytes)`. Verify with `crypto.timingSafeEqual`. Retry policy: 3 attempts, 0s / 5s / 30s backoff, 10s timeout per attempt; queue is persisted across restarts. Payload: ```json { "event": "invoice.status_changed", "invoice": { "id": 42, "external_order_id": "order_123", "amount": "15000.00", "status": "paid", "client_name": "John Doe", "client_phone": "87001234567", "paid_at": "2026-05-14T14:35:00Z" }, "timestamp": "2026-05-14T14:35:01Z" } ``` ## Status vocabulary Canonical invoice statuses (apipay-aligned): - `pending` — awaiting payment (non-terminal) - `cancelling` — cancel in progress (non-terminal) - `paid` — payment completed (terminal) - `cancelled` — cancelled by user / merchant (terminal) - `expired` — TTL expired or payment-failure terminal (terminal) - `partially_refunded` — at least one refund, balance remains (terminal) - `refunded` — fully refunded (terminal) ## Examples ```bash # Create a sandbox invoice curl -X POST https:///api/v1/invoices \ -H 'X-API-Key: kpa_test_...' \ -H 'Content-Type: application/json' \ -d '{"amount":15000,"phone_number":"87001234567","description":"Order #123","external_order_id":"order_123"}' ``` ```javascript // Verify a webhook signature (Node.js) import crypto from 'crypto'; export function verifyWebhook(rawBody, header, secret) { const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); const a = Buffer.from(header || ''); const b = Buffer.from(expected); return a.length === b.length && crypto.timingSafeEqual(a, b); } ``` See [docs/examples/](docs/examples/) for more (placeholder — examples will be added alongside future SDKs).