Skip to content
Developers

REST, JSON and one webhook.

The minimum that is genuinely enough for production: create a payment, get confirmation, close the order.

  1. 01

    Keys

    Request access, connect an IBAN, take sk_test from the dashboard.

  2. 02

    Payment

    POST /v1/invoices with amount and order number. You get a pay_url.

  3. 03

    Webhook

    Receive invoice.paid, verify the signature, close the order.

POST

Create a payment

Amount in minor units, reference is your order number. Repeating the request with the same reference returns the same payment.

/v1/invoices
curl -X POST https://api.kvitnyk.com/v1/invoices \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 124000,
    "currency": "UAH",
    "reference": "A-10427",
    "description": "Order A-10427",
    "callback_url": "https://shop.ua/hooks/kvitnyk"
  }'

{
  "id": "inv_8Qk2m",
  "status": "pending",
  "pay_url": "https://pay.kvitnyk.com/8Qk2m",
  "qr_payload": "BCD002UCT...",
  "expires_at": "2026-09-12T18:40:00Z"
}
WEBHOOK

Payment confirmation

Fires once the incoming transfer matches the open payment. Verify the signature header before processing.

X-Kvitnyk-Signature: sha256=...
POST /hooks/kvitnyk

{
  "event": "invoice.paid",
  "invoice_id": "inv_8Qk2m",
  "reference": "A-10427",
  "amount": 124000,
  "paid_amount": 124000,
  "currency": "UAH",
  "payer_bank": "monobank",
  "payer_name": "I*** K***",
  "matched_by": "reference",
  "verified_at": "2026-09-12T18:12:41Z"
}

HTTP 200 → delivered
otherwise: 5 retries, 1m → 1h

Payment statuses

pendingPayment issued, nothing received yet.
paidAmount matched — payment verified, webhook sent.
reviewMoney arrived but did not reconcile automatically: a different reference, a transfer from a third party, or several near-identical amounts. It waits for a decision in the dashboard — one click confirms it manually, and the webhook goes out after that.
underpaidLess arrived than issued. The payload carries the actual sum.
overpaidMore arrived than issued. You return the difference yourself.
expiredThe payment window closed with no transfer.
refundedYou refunded the customer — marked manually or via API.
  • Sandbox

    Separate keys and emulated transfers: you can walk every status without moving real money.

  • Idempotency

    Reference is the uniqueness key. Repeat calls create no second payment and no duplicate webhook.

  • Ready-made modules

    WooCommerce, Prom, Shopify, plus examples for Node, PHP, Python and Laravel.

  • API limits

    100 requests/min per key. Webhooks retry 5 times with backoff up to one hour.

Next step

Keys and sandbox the same day.

A request with no calls and no contracts. Test keys go out the same day; production follows once your account is connected.