Kutanapay Merchant API
    • Getting Started
    • Authentication
    • Use the API in your application
    • Webhooks
      • Webhooks Events
    • Merchant Checkout
      • Create checkout
        POST
      • List checkouts
        GET
      • Get checkout
        GET
      • Cancel checkout
        POST
      • Create checkout button embed session
        POST
    • Webhook Events
      • Checkout created
      • Checkout paid
      • Customer says they paid
      • Checkout approved
      • Checkout rejected
      • Checkout failed
      • Checkout timed out
      • Checkout expired
      • Checkout refunded
      • A second collection arrived
      • Test event
    • Schemas
      • RequestBodies
        • CheckoutEvent
      • DetailError
      • WebhookEnvelope
      • CheckoutEventData

    Webhooks

    Webhooks notify your server when checkout state changes. Register webhooks in the merchant platform (URL, subscribed events, secret). This page describes what your server must implement.

    Platform setup#

    In the merchant platform:
    1.
    Add an HTTPS endpoint URL (publicly reachable).
    2.
    Choose subscribed events (start with checkout.paid and checkout.failed).
    3.
    Copy the webhook secret and store it on your server for signature verification.
    4.
    Use Send test or delivery logs in the platform to debug.
    Webhook management APIs are intentionally not part of the public Checkout API reference.

    Incoming request#

    KutanaPay sends POST to your URL with headers:

    Payload envelope#

    {
      "version": "v1",
      "event_type": "checkout.paid",
      "timestamp": "2025-11-17T12:34:56Z",
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "merchant_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "data": { }
    }
    data holds event-specific fields (checkout id, amount, currency, status, customer email when present).

    Checkout events (common)#

    See Webhook event reference for the full catalog, field tables, and a sample checkout.paid payload.
    Start with: checkout.paid, checkout.approved, checkout.rejected, and optionally checkout.created.

    Verify the signature#

    Always verify X-Webhook-Signature before trusting the body.
    1.
    Read the raw request body as bytes or UTF-8 string (before JSON parsing).
    2.
    Header format: sha256=<hex>.
    3.
    Compute HMAC-SHA256 of the raw body using your webhook secret.
    4.
    Compare with constant-time equality (hmac.compare_digest in Python).

    Python#

    Use the raw body from the request (request.get_data(as_text=True) in Flask, await request.body() in FastAPI before parsing JSON).

    Node.js#

    Handler checklist#

    1.
    Verify signature; return 401 if invalid.
    2.
    Optionally reject stale timestamp (replay protection).
    3.
    Dedupe using idempotency_key (store processed keys).
    4.
    Return 2xx quickly; heavy work can run async.
    5.
    Use 5xx only for temporary failures (KutanaPay retries). Use 4xx for permanent rejections.

    Retries#

    Failed deliveries are retried with backoff. Make handlers idempotent.

    Troubleshooting#

    IssueCheck
    Signature failsRaw body vs re-serialized JSON; wrong secret; header name case
    No deliveriesHTTPS URL; firewall; URL reachable from internet
    Disabled webhookPlatform delivery logs after repeated failures
    Modified at 2026-09-26 20:44:39
    Previous
    Use the API in your application
    Next
    Webhooks Events
    Built with