Checkout Link
Request
Request to generate a checkout page session URL
Path
POST /checkout/link
Example (Basic)
Basic example to create a one-time payment link with a fixed amount.
{
"type": "CHOOSE_WHAT_TO_PAY",
"item": {
"title": "My product name",
"frequency": ["ONE_TIME"], // [optional] default "ONE_TIME"
"amount": "1200.00" // [optional] preset an amount to pay else an amount field will show for the user to enter
}
}
Example (Donation form)
An example of a donation form with one-time, monthly and annual subscription options and an amount field the user can select.
Flexible checkouts (CHOOSE_WHAT_TO_PAY) use a single item object for the payer-facing offer. customization.confirmation_message appears after successful payment unless you rely on redirects. Customer handling: send customer_id for an existing record, send a customer object to create or prefill, or omit both and optionally supply customer_fields for extra form fields (name, surname, and email always show).
{
"type": "CHOOSE_WHAT_TO_PAY",
// Pass your product / campaign details via item
"item": {
"title": "My product name", // Product title, will appear on invoice
"description": "My product description", // [optional] longer description
"frequency": ["ONE_TIME", "MONTHLY", "ANNUALLY"], // options the payer may choose among
"amount_options": [50, 100, 150, 200, 250], // [optional] preset amounts (max five)
"amount_max": 3000 // [optional] cap when payer enters any amount
},
"customization": {
"button_text": "Donate now", // [optional] primary button label — see Advanced for full options
"confirmation_message": "Thank you for your donation" // [optional] shown after payment; else use redirects
},
"signature": "secret-key-for-payload", // [optional] payload signature when configured
"notification": {
"email": "me@my-email.co.za", // [optional] email notifications on successful payment
"webhook_url": "https://merchant.example/webhooks/payments" // webhook URL for each successful payment
},
// Issue and optionally email an invoice after payment
"invoice": {
"is_generate": true,
"is_send": true // send paid invoice to the customer when true
},
// If neither customer_id nor customer is passed, show a form — these are extra collectable fields
"customer_fields": [
"CONTACT_NUMBER",
"ID_NUMBER",
"COMPANY_NAME",
"COMPANY_REG",
"VAT_NUMBER",
"BILLING_ADDRESS",
"SHIPPING_ADDRESS"
]
}
Example (Products, subscriptions and invoices)
Example with a recurring and one-time product to create a subscription with invoices.
{
"type": "PRODUCTS_AND_SUBSCRIPTIONS",
"customization": {
"button_text": "Pay now" // [optional] button text — default label if omitted depends on checkout
},
"signature": "secret-key-for-payload", // [optional] shared-secret signature for the payload
"customer_id": "cus_abc123...", // existing customer record; alternatively pass full customer object
// Catalog lines (one-off and recurring products may be mixed — see Products reference)
"items": [
{
"product_id": "pro_abc123...",
"qty": 2 // default 1 when omitted
},
{
"product_id": "pro_xyz789..."
}
],
// Invoice for this checkout / subscriptions created from it
"invoice": {
"is_generate": true, // generate invoice for the basket or subscription billing
"is_send": true // email invoice when paid
},
// Redirects after payer finishes or taps cancel
"redirects": {
"success_url": "https://merchant.example/success?myquery=myparam", // may append &signature=
"cancel_url": "https://merchant.example/cancel"
}
}
Example (Advanced )
Advanced example with example with all options
{
"type": "PRODUCTS_AND_SUBSCRIPTIONS",
"signature": "secret-key-for-payload", // [optional] shared-secret signature for the payload, see the signature section below
// Inline customer create / prefilled profile (alternative: customer_id)
"customer": {
"reference": "CLN240919000001",
"company_name": null,
"person_name": "John",
"person_surname": "Doe",
"client_type": "RESIDENT_INDIVIDUAL", // [optional]
"id_type": "SOUTH_AFRICAN_ID",
"id_number": "8007014800087",
"email": "johndoe@mail.com",
"contact_number": "+27831234567",
"billing_address": {
"line1": "1 Loop Street",
"line2": "Floor 5",
"city_or_town": "Cape Town",
"province_or_state": "Western Cape",
"postal_or_zip_code": "8001",
"country": "ZA"
},
"shipping": {
"contact_name": "Jane Doe",
"contact_number": "+27820000000",
"address": {
"line1": "1 Loop Street",
"line2": "Floor 5",
"city_or_town": "Cape Town",
"province_or_state": "Western Cape",
"postal_or_zip_code": "8001",
"country": "ZA"
}
},
"metadata": {
"custom_field_1": "custom data 1",
"custom_field_2": "custom data 2"
}
},
// Basket built from catalogue products — discount per line optional
"items": [
{
"product_id": "pro_abc123...",
"qty": 2,
"discount": 100 // discount amount for this item (advanced use)
},
{
"product_id": "pro_xyz789...",
"qty": 3
},
// ...when no product_id is passed a product can be created
{
"name": "Generic Wooden Ball",
"description": "Good For Training And Recreational Purposes",
"code": "PRD-606075",
"unit_label": "each",
"price_excl": "2409.86",
"is_tax": false,
"pricing_type": "RECURRING",
"product_status": "ACTIVE",
"billing_period": "MONTHLY",
"custom_period": null,
"custom_interval": null,
"picture_default": null,
"pictures": {} // {...} media bundle when uploading gallery assets programmatically
}
],
"invoice": {
"is_generate": true,
"is_send": true
},
// Restrict rails shown — omit array to expose every method toggled on for the merchant
"payment": {
"payment_methods": ["CARD", "ZAPPER"]
},
"redirects": {
// Successful redirect URLs may append `signature` when signatures are configured.
"success_url": "https://merchant.example/success?myquery=myparam",
"cancel_url": "https://merchant.example/cancel"
},
"settings": {
"expiry_time": 1440 // minutes until checkout link lapses — omit when the link never expires (default)
},
"customization": {
"button_text": "Pay",
"type": "PAGE", // or "EMBED" for iframe-style hosting or in-app payments
"is_display_cancel_button": true,
"is_display_total": true,
"is_display_items": true, // cart line detail list
"brand": {
"primary": "#00DC82",
"secondary": "#CCCCCC"
}
}
}
Example (Embed payments)
Embed payments into your mobile app or frameworks like React, Angular or Vue
{
"type": "PRODUCTS_AND_SUBSCRIPTIONS",
"customization": {
"button_text": "Pay now", // [optional] button text — default label if omitted depends on checkout
"type": "EMBED", // show embedded screen that can be displayed in a iFrame or WebView, this will also send message events instead of a redirect
"is_display_cancel_button": false, // hide the cancel button, if true will trigger a 'payment.cancelled' message
"is_display_total": false, // hide the total amount
"is_display_items": false, // hide the checkout items
"brand": {
"primary": "#00DC82", // brand according to your app
"secondary": "#CCCCCC"
}
},
"customer_id": "cus_abc123...", // existing customer record; alternatively pass full customer object
// Catalog lines (one-off and recurring products may be mixed — see Products reference)
"items": [
{
"product_id": "pro_abc123...",
"qty": 2 // default 1 when omitted
},
{
"product_id": "pro_xyz789..."
}
],
// Invoice for this checkout / subscriptions created from it
"invoice": {
"is_generate": true, // generate invoice for the basket or subscription billing
"is_send": true // email invoice when paid
},
}
The following message events will be sent to your app via a native bridge; use them to drive the next action:
payment.loadedpayment.cancelledpayment.failedpayment.successfulpayment.pendingpayment.closed
The following data will be sent in the message on payment.successful events to verify the transaction:
{
"source": "kwik-payments",
"event": "payment.successful",
"sessionId": "ses_abc123...",
"transactionId": "tra_abc123...",
"customerId": "cus_abc123...",
"status": "PAID"
}
The following data will be sent in the message on payment.failed events:
{
"source": "kwik-payments",
"event": "payment.failed",
"sessionId": "ses_abc123...",
"transactionId": "tra_abc123...",
"customerId": "cus_abc123...",
"status": "DECLINED",
"message": "Your transaction was declined due to insufficient funds"
}
Request Parameters
Fields below appear in the request body examples on this page. Y = required for all requests, C = required or applicable depending on type and whether you pass customer_id vs inline customer.
Field | Required | Type | Description | Example |
|---|---|---|---|---|
| type | Y | ENUM | Checkout mode: CHOOSE_WHAT_TO_PAY (flexible / donation-style) or PRODUCTS_AND_SUBSCRIPTIONS (catalog line items) | CHOOSE_WHAT_TO_PAY |
| item | C | Object | Present when type is CHOOSE_WHAT_TO_PAY | See basic and donation examples |
| item.title | C | String | Product or payment title; shown on the checkout and invoice | My product name |
| item.description | N | String | Optional longer product description | My product description |
| item.frequency | N | Array | Frequencies the payer can choose (e.g. one-time and subscriptions). Defaults to ONE_TIME if omitted | ["ONE_TIME", "MONTHLY", "ANNUALLY"] |
| item.amount | N | String | Preset amount; if omitted the user can enter an amount | 1200.00 |
| item.amount_options | N | Array | Preset amounts the user can pick from, max 5 amounts | [50.00, 100.00, 150.00, 200.00, 250.00] |
| item.amount_max | N | Number | Maximum amount allowed when the user enters an amount | 3000.00 |
| items | C | Array | Present when type is PRODUCTS_AND_SUBSCRIPTIONS; each row either references product_id or defines an inline product (omit product_id), see products | See examples |
| items.product_id | C | String | Catalog product id when referencing an existing product; omit for an inline-defined line item | pro_abc123... |
| items.qty | N | Integer | Quantity for this line item (default 1) | 2 |
| items.discount | N | Number | Discount amount for catalog lines (product_id present), advanced use | 100.00 |
| items.name | C | String | Product name when defining an inline item (no product_id) | Generic Wooden Ball |
| items.description | N | String | Inline item description | Good For Training And Recreational Purposes |
| items.code | C | String | Unique product code; record may be created when unique | PRD-606075 |
| items.unit_label | N | String | Unit label | each |
| items.price_excl | C | String | Price excluding tax | 2409.86 |
| items.is_tax | N | Boolean | Whether tax applies | false |
| items.pricing_type | C | ENUM | e.g. RECURRING or one-time as per your catalog | RECURRING |
| items.product_status | N | ENUM | e.g. ACTIVE | ACTIVE |
| items.billing_period | C | String | When recurring (e.g. MONTHLY) | MONTHLY |
| items.custom_period | N | null | Custom billing period detail if applicable | null |
| items.custom_interval | N | null | Custom interval if applicable | null |
| items.picture_default | N | null | Default image reference | null |
| items.pictures | N | Object | Gallery or image payload | {} |
| signature | N | String | Optional passphrase signature for verifying the payload | secret-key-for-payload |
| customer_id | C | String | Use an existing customer record instead of customer inline object | cus_abc123... |
| customer_fields | N | Array | Extra fields on the payer form when neither customer_id nor customer is supplied; name, surname and email always show | ["CONTACT_NUMBER", "ID_NUMBER", "COMPANY_NAME", "COMPANY_REG", "VAT_NUMBER", "BILLING_ADDRESS", "SHIPPING_ADDRESS"] |
| customer | C | Object | Create or prefill payer details when not using only customer_id | See advanced example |
| customer.reference | C | String(35) | Your reference for this customer | CLN240919000001 |
| customer.company_name | C | String(64) | Company name when applicable | null |
| customer.person_name | C | String(32) | First name | John |
| customer.person_surname | C | String(32) | Surname | Doe |
| customer.client_type | N | ENUM | RESIDENT_INDIVIDUAL, NON_RESIDENT_INDIVIDUAL, DOMESTIC_COMPANY, FOREIGN_COMPANY, FINANCIAL_INSTITUTION | RESIDENT_INDIVIDUAL |
| customer.id_type | N | ENUM | SOUTH_AFRICAN_ID, PASSPORT_NUMBER, TEMPORARY_RESIDENCE, COMPANY_REGISTRATION_NUMBER | SOUTH_AFRICAN_ID |
| customer.id_number | N | String(13) | Identity or registration number | 8007014800087 |
| customer.email | C | String(128) | Email address | johndoe@mail.com |
| customer.contact_number | C | String(16) | Contact number | +27831234567 |
| customer.billing_address.line1 | N | String | Billing line 1 | 1 Loop Street |
| customer.billing_address.line2 | N | String | Billing line 2 | Floor 5 |
| customer.billing_address.city_or_town | N | String | City or town | Cape Town |
| customer.billing_address.province_or_state | N | String | Province or state | Western Cape |
| customer.billing_address.postal_or_zip_code | N | String | Postal or ZIP code | 8001 |
| customer.billing_address.country | N | String(2) | ISO 3166-1 alpha-2 country code | ZA |
| customer.shipping.contact_name | N | String | Shipping contact name | Jane Doe |
| customer.shipping.contact_number | N | String(16) | Shipping contact number | +27820000000 |
| customer.shipping.address.line1 | N | String | Shipping line 1 | 1 Loop Street |
| customer.shipping.address.line2 | N | String | Shipping line 2 | Floor 5 |
| customer.shipping.address.city_or_town | N | String | Shipping city | Cape Town |
| customer.shipping.address.province_or_state | N | String | Shipping province or state | Western Cape |
| customer.shipping.address.postal_or_zip_code | N | String | Shipping postal code | 8001 |
| customer.shipping.address.country | N | String(2) | ISO 3166-1 alpha-2 | ZA |
| customer.metadata | N | Object | Custom key/value metadata | {"custom_field_1": "custom data 1"} |
| invoice.is_generate | C | Boolean | Whether to generate an invoice for the payment or subscription | true |
| invoice.is_send | N | Boolean | Whether to send the paid invoice to the customer | true |
| notification.email | N | String | Email address to notify on each successful payment | me@my-email.co.za |
| notification.webhook_url | N | String | Webhook URL for each successful payment | https://merchant.example/webhooks/payments |
| redirects.success_url | N | String | Redirect after successful payment; &signature= appended if configured | https://merchant.example/success?myquery=myparam |
| redirects.cancel_url | N | String | Redirect if the payer cancels | https://merchant.example/cancel |
| payment.payment_methods | N | Array | Limit methods shown (CARD, ZAPPER, SNAPSCAN, APPLE_PAY, GOOGLE_PAY, SCAN_TO_PAY, 1VOUCHER, SCODE, MONEY_BADGER) | [CARD, ZAPPER] |
| settings.expiry_time | N | Integer | Link lifetime in minutes; omit so the link does not expire (per advanced example comment) | 1440 |
| customization.button_text | N | String | Primary action button label | Pay |
| customization.confirmation_message | N | String | Message after successful payment (donation example); otherwise use redirects | Thank you for your donation |
| customization.type | N | ENUM | Create a link for a PAGE or pass through EMBED for iframe or in-app payments. The EMBED type pages will not redirect you but transmit a message via the window postMessage() method | PAGE |
| customization.is_display_cancel_button | N | Boolean | Show cancel control | true |
| customization.is_display_total | N | Boolean | Show total | true |
| customization.is_display_items | N | Boolean | Show line items | true |
| customization.brand.primary | N | String | Primary hex colour | #00DC82 |
| customization.brand.secondary | N | String | Secondary hex colour | #CCCCCC |
Response Body
{
"status": true,
"result": {
"id": "chk_HVpCeoNys1f22X7QcuWHY",
"session_id": "ses_G-xkVKoxHgEBrY8suKgR3",
"amount": "1200.00",
"currency": "ZAR",
"customer_id": "cus_abc123...",
"link_url": "https://pay.kwik.co.za/checkout/cs_test_a1b2c3",
"expires_at": "2025-09-13T12:30:00Z"
}
}
Response Parameters
Field | Type | Description | Example |
|---|---|---|---|
| status | Boolean | Indicates if the request was successful | true |
| result.id | String | Unique checkout session identifier | chk_HVpCeoNys1f22X7QcuWHY |
| result.session_id | String (32) | Session identifier for the checkout | ses_G-xkVKoxHgEBrY8suKgR3 |
| result.amount | String | Checkout amount | 1200.00 |
| result.currency | String(32) | ISO 4217 currency code | ZAR |
| result.customer_id | String(32) | Associated customer ID | cus_abc123... |
| result.link_url | String | URL to redirect user to complete checkout | https://pay.kwik.co.za/checkout/cs_test_a1b2c3 |
| result.expires_at | String | ISO timestamp when the checkout session expires | 2025-09-13T12:30:00Z |
Signature creation
When creating API keys on the dashboard you can download a passphrase key, use the key in the examples below to generate your signature and send it in the signature parameter:
const crypto = require('crypto')
function canonicalize(value) {
if (value === null || typeof value !== 'object') {
return JSON.stringify(value)
}
if (Array.isArray(value)) {
return `[${value.map(canonicalize).join(',')}]`
}
return `{${Object.keys(value)
.filter((key) => key !== 'signature' && value[key] !== undefined)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalize(value[key])}`)
.join(',')}}`
}
function generateKwikSignature(payload, passphrase) {
const canonicalPayload = canonicalize(payload)
return crypto
.createHmac('sha256', passphrase)
.update(canonicalPayload)
.digest('hex')
}
// Usage example
const payload = {
type: 'CHOOSE_WHAT_TO_PAY',
item: {
title: 'My product name',
frequency: ['ONE_TIME'], // optional
amount: '1200.00' // optional
}
}
// get this from the dashboard when creating an API key
const passphrase = 'your-secure-passphrase'
const signature = generateKwikSignature(payload, passphrase)
const requestBody = {
...payload,
signature
}
console.log('Canonical payload:', canonicalize(payload))
console.log('Signature:', signature)
console.log('Request body:', requestBody)
<?php
function canonicalize($value) {
if ($value === null) return 'null';
if (is_bool($value)) return $value ? 'true' : 'false';
if (is_int($value) || is_float($value)) return json_encode($value);
if (is_string($value)) return json_encode($value);
if (is_array($value)) {
$isList = array_keys($value) === range(0, count($value) - 1);
if ($isList) {
return '[' . implode(',', array_map('canonicalize', $value)) . ']';
}
ksort($value);
$parts = [];
foreach ($value as $key => $item) {
if ($key === 'signature') continue;
$parts[] = json_encode((string)$key) . ':' . canonicalize($item);
}
return '{' . implode(',', $parts) . '}';
}
return json_encode($value);
}
function generateKwikSignature($payload, $passphrase) {
return hash_hmac('sha256', canonicalize($payload), $passphrase);
}
// Usage
$payload = [
"type" => "CHOOSE_WHAT_TO_PAY",
"item" => [
"title" => "My product name",
"frequency" => ["ONE_TIME"],
"amount" => "1200.00"
]
];
$passphrase = 'your-secure-passphrase';
$signature = generateKwikSignature($payload, $passphrase);
$requestBody = array_merge($payload, [
"signature" => $signature
]);
print_r($requestBody);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
public class KwikSignature
{
public static string Canonicalize(object value)
{
if (value == null) return "null";
if (value is string || value is bool || value is int || value is long || value is decimal || value is double)
return JsonSerializer.Serialize(value);
if (value is IEnumerable<object> list)
return "[" + string.Join(",", list.Select(Canonicalize)) + "]";
if (value is IDictionary<string, object> dict)
{
var parts = dict
.Where(kvp => kvp.Key != "signature")
.OrderBy(kvp => kvp.Key)
.Select(kvp => JsonSerializer.Serialize(kvp.Key) + ":" + Canonicalize(kvp.Value));
return "{" + string.Join(",", parts) + "}";
}
return JsonSerializer.Serialize(value);
}
public static string GenerateKwikSignature(IDictionary<string, object> payload, string passphrase)
{
var canonical = Canonicalize(payload);
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(passphrase));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(canonical));
return Convert.ToHexString(hash).ToLowerInvariant();
}
public static void Main()
{
var payload = new Dictionary<string, object>
{
["type"] = "CHOOSE_WHAT_TO_PAY",
["item"] = new Dictionary<string, object>
{
["title"] = "My product name",
["frequency"] = new List<object> { "ONE_TIME" },
["amount"] = "1200.00"
}
};
var passphrase = "your-secure-passphrase";
var signature = GenerateKwikSignature(payload, passphrase);
payload["signature"] = signature;
Console.WriteLine(JsonSerializer.Serialize(payload));
}
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class KwikSignature {
public static String canonicalize(Object value) {
if (value == null) return "null";
if (value instanceof String) return "\"" + value + "\"";
if (value instanceof Boolean || value instanceof Number) return value.toString();
if (value instanceof List<?>) {
List<?> list = (List<?>) value;
List<String> parts = new ArrayList<>();
for (Object item : list) parts.add(canonicalize(item));
return "[" + String.join(",", parts) + "]";
}
if (value instanceof Map<?, ?> map) {
List<String> keys = new ArrayList<>();
for (Object k : map.keySet()) {
if (!"signature".equals(k)) keys.add((String) k);
}
Collections.sort(keys);
List<String> parts = new ArrayList<>();
for (String key : keys) {
parts.add("\"" + key + "\":" + canonicalize(map.get(key)));
}
return "{" + String.join(",", parts) + "}";
}
return "\"" + value.toString() + "\"";
}
public static String generateKwikSignature(Map<String, Object> payload, String passphrase) throws Exception {
String canonical = canonicalize(payload);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(passphrase.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) hex.append(String.format("%02x", b));
return hex.toString();
}
public static void main(String[] args) throws Exception {
Map<String, Object> payload = new HashMap<>();
payload.put("type", "CHOOSE_WHAT_TO_PAY");
Map<String, Object> item = new HashMap<>();
item.put("title", "My product name");
item.put("frequency", List.of("ONE_TIME"));
item.put("amount", "1200.00");
payload.put("item", item);
String passphrase = "your-secure-passphrase";
String signature = generateKwikSignature(payload, passphrase);
payload.put("signature", signature);
System.out.println(payload);
}
}
import json
import hmac
import hashlib
def canonicalize(value):
if value is None or not isinstance(value, (dict, list)):
return json.dumps(value, separators=(',', ':'))
if isinstance(value, list):
return '[' + ','.join(canonicalize(v) for v in value) + ']'
return '{' + ','.join(
json.dumps(k) + ':' + canonicalize(value[k])
for k in sorted(value.keys())
if k != 'signature'
) + '}'
def generate_kwik_signature(payload, passphrase):
canonical = canonicalize(payload)
return hmac.new(
passphrase.encode(),
canonical.encode(),
hashlib.sha256
).hexdigest()
# Usage
payload = {
"type": "CHOOSE_WHAT_TO_PAY",
"item": {
"title": "My product name",
"frequency": ["ONE_TIME"],
"amount": "1200.00"
}
}
passphrase = "your-secure-passphrase"
signature = generate_kwik_signature(payload, passphrase)
request_body = {
**payload,
"signature": signature
}
print(request_body)
Webhook
When a checkout session is completed, updated, or expires, a webhook may be delivered to notification.webhook_url when that field was supplied on create. Broader platform webhooks are configured separately if applicable.
Possible event values include checkout.completed, checkout.expired, checkout.failed, and others listed under Webhook events. The subscription object is present only when a recurring collection is created.
Webhook Payload
{
// checkout.completed · checkout.expired · checkout.failed · etc.
"event": "checkout.completed",
"data": [
{
"checkout": {
"id": "chk_HVpCeoNys1f22X7QcuWHY",
"session_id": "ses_G-xkVKoxHgEBrY8suKgR3",
"amount": "1200.00",
"currency": "ZAR",
"customer_id": "cus_abc123...",
"card_id": "crd_SFq2E9LskQimPkf2mRqnV", // populated for card tenders
"transaction_id": "tra_pr6CvR_4pvWwmgQ4y3dtY",
"transaction_status": "ACTIVE",
"expires_at": "2025-09-13T12:30:00Z",
"completed_at": "2025-09-13T10:15:30Z"
},
// Only present once a recurring collection / mandate results from checkout
"subscription": {
"id": "col_Pw5jImVFZfpd0Lplp35op",
"amount": "1200.00",
"currency": "ZAR",
"subscription_status": "ACTIVE",
"payment_method": "CARD",
"payment_method_id": "pam_ak7lHmJ0a2fuD1q4M6b0q",
"created_at": "2025-09-13T10:15:30Z"
}
}
],
"created_at": "2025-09-13T12:30:00Z"
}
Webhook Payload Parameters
Field | Type | Description | Example |
|---|---|---|---|
| event | String | Type of webhook event that occurred | checkout.completed |
| data | Array | Array containing checkout and payment data | ... |
| data.checkout.id | String | Unique checkout session identifier | chk_HVpCeoNys1f22X7QcuWHY |
| data.checkout.session_id | String | Session identifier for the checkout | ses_G-xkVKoxHgEBrY8suKgR3 |
| data.checkout.amount | String | Checkout amount | 1200.00 |
| data.checkout.currency | String(3) | ISO 4217 currency code | ZAR |
| data.checkout.customer_id | String(32) | Associated customer ID | cus_abc123... |
| data.checkout.card_id | String | Card identifier when payment was made with a saved or newly created card | crd_SFq2E9LskQimPkf2mRqnV |
| data.checkout.transaction_id | String | Transaction identifier | tra_pr6CvR_4pvWwmgQ4y3dtY |
| data.checkout.transaction_status | ENUM | Transaction status, see lookups | ACTIVE |
| data.checkout.expires_at | String | ISO timestamp when checkout session expires | 2025-09-13T12:30:00Z |
| data.checkout.completed_at | String | ISO timestamp when checkout was completed | 2025-09-13T10:15:30Z |
| data.subscription.id | String | Subscription identifier | col_Pw5jImVFZfpd0Lplp35op |
| data.subscription.amount | String | Subscription amount | 1200.00 |
| data.subscription.currency | String(3) | ISO 4217 currency code | ZAR |
| data.subscription.subscription_status | ENUM | Subscription status, see lookups | ACTIVE |
| data.subscription.payment_method | String | Payment method used | CARD |
| data.subscription.payment_method_id | String | Payment method id used | pam_ak7lHmJ0a2fuD1q4M6b0q |
| data.subscription.created_at | String | ISO timestamp when payment was created | 2025-09-13T10:15:30Z |
| created_at | String | ISO timestamp when webhook was created | 2025-09-13T10:15:30Z |
Webhook Events
| Event | Description | Trigger Condition | Data Included |
|---|---|---|---|
checkout.completed | Checkout session was successfully completed with payment | When customer completes payment successfully | Checkout details, payment information, transaction data |
checkout.expired | Checkout session expired without completion | When checkout session reaches expiry time without payment | Checkout details only, no payment data |
checkout.failed | Checkout session failed due to payment failure | When payment processing fails (declined card, insufficient funds, etc.) | Checkout details, failed payment attempt, error information |
checkout.abandoned | Customer abandoned the checkout process | When customer leaves checkout page without completing | Checkout details, abandonment timestamp |
checkout.pending | Payment is pending additional verification | When payment requires manual review or 3DS authentication | Checkout details, pending payment status |
checkout.cancelled | Checkout was cancelled by customer or system | When cancel button is used or system cancels due to fraud | Checkout details, cancellation reason |
Webhook Security
All webhooks are sent with the following headers for verification:
X-Signature: HMAC-SHA256 signature of the payloadX-Timestamp: Unix timestamp of when the webhook was sentUser-Agent:Kwik-Webhooks/1.0
Webhook Response
Your endpoint should respond with a 200 status code to acknowledge receipt. Failed webhooks will be retried up to 3 times with exponential backoff.
Transactions
Retrieve and monitor transaction records including payment attempts, status changes, and settlement details. Access comprehensive transaction history with filtering, pagination, and detailed status information for reporting and reconciliation.
Checkout Form
Create a secure, customizable checkout form session to capture payments with cards, bank transfers, and other payment methods. Supports 3D Secure authentication, cards storage for recurring billing, invoice issuance, customer creation, and webhook notifications.