Checkouts let you send your customer to a hosted payment page on KutanaPay to pay you. One checkout is one payment session: create it from your server, share the checkout_url, and track the outcome with webhooks (and optional API reads).If you have not done so yet, read Authentication (API keys) and Webhooks (callbacks and signatures) before building this flow.
What hosted checkout is#
Your backend calls POST /api/v1/merchants/checkouts (API host, e.g. https://dev.kutanapay.com).
KutanaPay returns data.checkout_url: a link to the customer-facing app (not the API host).
The customer opens that link in a browser and pays on the hosted page.
Your webhook handler receives events such as checkout.paid and updates your order.
The customer never sees your x-api-key. Only your server uses the key.
What the customer sees#
With one API call and one link, the customer can pay you.1.
Open the link you sent (email, SMS, or redirect from your site).
2.
Choose how to pay (mobile money, bank transfer, or other methods enabled for that currency).
3.
Complete payment on the hosted page (for example approve a mobile money prompt on their phone).
4.
Success or closed state on the page; your branding redirect URL may send them back to your site when configured in the merchant platform.
Screens and methods depend on currency, KYB, and branding on your merchant account.
Creating and using a checkout#
Step 1: Create the checkout#
Send a request to POST /api/v1/merchants/checkouts with header x-api-key.Typical e-commerce order (fixed amount and currency):Minimal request (only required fields):{
"customer_email": "customer@example.com",
"amount": 99.99,
"currency_code": "GHS"
}
You receive a response immediately:{
"status": "success",
"message": "Checkout created",
"data": {
"id": "c4e8b1a2-9f3d-4e7a-b6c5-1d2e3f4a5b6c",
"merchant_id": "7bb54443-6fc9-4a1d-ab46-d787ec90c87f",
"customer_email": "customer@example.com",
"amount": 150.0,
"currency_code": "GHS",
"status": "pending",
"payment_reference": "PAY-K7M2Q9X4",
"checkout_url": "https://dev.kutanapay.com/public/checkouts/PAY-K7M2Q9X4#token=9f3e2a1b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2",
"expires_at": "2026-03-26T12:40:00.000000",
"created_at": "2026-03-26T12:30:00.000000"
}
}
Store in your system before redirecting the customer:| Field | Use |
|---|
data.id | Checkout UUID for GET .../checkouts/{id} and cancel |
data.payment_reference | Human-readable ref (PAY-…) |
data.checkout_url | Full link to send to the customer |
meta_data.order_id (if you sent it) | Tie webhooks back to your order |
The checkout_url host comes from KutanaPay configuration (DOMAIN_NAME on the API). Use the URL exactly as returned. Do not build links from payment_reference alone.Request parameters#
| Parameter | Required | Behaviour |
|---|
customer_email | Yes | Customer email |
amount | Yes | Positive number |
currency_code | Yes | Supported code: GHS, KES, NGN, USD, EUR, GBP, ETB, etc. |
customer_name | No | Shown where supported |
customer_phone | No | Useful for mobile money flows |
description | No | Short label for the payment |
expires_in_minutes | No | Overrides default session lifetime |
meta_data | No | Your JSON; included in webhook payloads where applicable |
Supported currencies and payment methods depend on your merchant account and KYB.Step 2: Send the customer to the payment page#
Forward the customer to data.checkout_url.| Pattern | Example |
|---|
| HTTP redirect | After create, respond with 302 Location: {checkout_url} |
| Pay button | Link href="{checkout_url}" on your site |
| Email / SMS | Paste the full URL including #token=... |
The hosted page loads the session using the token in the URL, then calls KutanaPay public checkout APIs with header X-Checkout-Token. That token is not your merchant API key.Important: If the link is missing the #token= fragment (or legacy ?token=), the hosted page cannot open. Always use checkout_url from the create response.
Email clients: Some clients handle URL fragments poorly. Prefer redirect from your website after the customer clicks "Pay", or open the link from your own domain flow.
Step 3: Track checkout status#
A checkout has its own lifecycle. Prefer webhooks for fulfillment.| Approach | When to use |
|---|
Webhook checkout.paid (and related events) | Primary: mark orders paid, send receipts |
GET /api/v1/merchants/checkouts/{checkout_id} | Polling or support tools |
GET /api/v1/merchants/checkouts | List recent sessions |
Subscribe to webhook events in the merchant platform. See Webhook event reference for payload fields.Step 4: Cancel an unused checkout#
If the customer should not pay anymore and the checkout is still pending, call:Paid or completed checkouts cannot be cancelled.
Checkout lifecycle#
Statuses on the checkout record (exact set can vary by payment rail):| Status | Meaning |
|---|
pending | Created; waiting for customer payment |
awaiting_payment_initiation | Payment flow starting |
processing | Payment in progress (e.g. STK push sent) |
paid / completed | Payment recorded |
failed | Payment failed |
cancelled | Cancelled (merchant or customer flow) |
expired / timeout | Session or provider timeout |
Treat checkout.paid (webhook) as the main signal to fulfill orders. Use GET .../checkouts/{id} when you need to reconcile or recover from a missed webhook.
Return to your website#
If you set a success redirect URL in merchant platform branding, the customer may be sent there after a successful payment. That URL is separate from checkout_url.Do not rely only on the customer landing on your site. Always confirm payment with webhooks or a authenticated GET checkout call.
How to find out the result#
1.
Webhook (recommended)
Verify X-Webhook-Signature, read event_type, use idempotency_key so you process each delivery once.
See Webhooks and Webhook event reference.
2.
Poll checkout
GET /api/v1/merchants/checkouts/{checkout_id} with x-api-key.
3.
After expiry
Pending checkouts can expired. Handle checkout.expired or failed events if you subscribed to them.
Keeping your system consistent#
Handle statuses defensively:Paid: webhook checkout.paid (or checkout status paid / completed) → mark order paid.
Still open: pending, processing → keep order pending; wait or poll.
Closed without pay: cancelled, expired, failed → do not fulfill; let customer create a new checkout if needed.
Network errors on create: If you are unsure whether create succeeded, use GET .../checkouts/{id} only if you stored data.id. If you never got a response and have no id, list checkouts or create a new session according to your idempotency rules.Idempotency on webhooks: Store idempotency_key from each delivery before updating orders.
End-to-end example (merchant server)#
1. Customer clicks "Pay" on your site (order ORD-7842).
2. Your server POST /api/v1/merchants/checkouts with amount, currency, customer_email, meta_data.order_id.
3. Your server saves data.id and redirects browser to data.checkout_url.
4. Customer pays on hosted KutanaPay page.
5. Your webhook handler receives checkout.paid → verify signature → mark ORD-7842 paid.
See Use the API in your application for curl and Apidog Try it.
Branding, KYB, and go-live#
Logo, colors, and redirect URL are set in the merchant platform. Production checkouts may require approved KYB.
API reference#
OpenAPI on this site lists the four checkout endpoints with JSON examples (no full schema catalog):POST /api/v1/merchants/checkouts
GET /api/v1/merchants/checkouts
GET /api/v1/merchants/checkouts/{checkout_id}
POST /api/v1/merchants/checkouts/{checkout_id}/cancel
What to do next#
| Step | Guide |
|---|
| Configure webhooks | Webhooks |
| Event payloads | Webhook event reference |
| Try requests in Apidog | Use the API in your application |
Modified at 2026-09-26 21:58:52