Introduction

The KitaPay API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.

You can use the KitaPay API in test mode, which does not affect your live data or interact with the banking networks. The API key you use to authenticate the request determines whether the request is live mode or test mode.

Base URL
https://api.kitapay.com/v1

Authentication

The KitaPay API uses API keys to authenticate requests. You can view and manage your API keys in the KitaPay Dashboard.

Authentication to the API is performed via HTTP Bearer Auth. Provide your API key as the bearer token value.

Important: Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
Authentication Header
Authorization: Bearer sk_test_your_secret_key

Errors

KitaPay uses conventional HTTP response codes to indicate the success or failure of an API request.

2xxIndicates success.
400Bad Request. The request was unacceptable, often due to missing a required parameter.
401Unauthorized. No valid API key provided.
404Not Found. The requested resource doesn't exist.
5xxServer errors. Something went wrong on KitaPay's end.
Error Response Example
{
  "success": false,
  "error": {
    "code": "invalid_request_error",
    "message": "The amount must be greater than 10000",
    "param": "amount"
  }
}

Create Payment

POST

Creates a new payment transaction. This endpoint generates a QRIS string or payment link depending on your configuration.

Body Parameters

merchantIdrequired
string (UUID)

The unique identifier of your merchant account, available in your dashboard.

amountrequired
integer

The amount intended to be collected by this payment. Must be a positive integer in IDR.

externalIdrequired
string

A unique string referencing this payment in your own system. Prevents duplicate payments.

targetWebhookUrl
string (URL)

The URL where KitaPay will send a POST request when the payment status changes (e.g. successfully paid).

curl -X POST https://api.kitapay.com/v1/payments \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId": "b3f0-4567-89ab",
    "amount": 150000,
    "externalId": "ORDER-12345",
    "targetWebhookUrl": "https://api.yoursite.com/webhook"
  }'
Response
{
  "success": true,
  "transaction": {
    "id": "kp_txn_9a8b7c6d",
    "external_id": "ORDER-12345",
    "amount": 150000,
    "status": "PENDING",
    "qris_string": "00020101021126660016...",
    "expires_at": "2026-08-30T18:00:00Z"
  }
}

Snap UI Integration

Once a payment is created via the backend API, you can display the KitaPay Snap UI to your customers. It's a ready-to-use checkout interface that securely handles the QRIS display and status polling.

Opening the Snap Popup

In your frontend (React, Vue, or Vanilla JS), simply open a popup window pointing to the Snap URL using the transaction.id you received from the Create Payment API.

Listening for Success Events

The Snap window will communicate back to your main window via the browser's postMessage API when a payment succeeds.

Frontend Javascript
// 1. Open Snap UI Popup
const transactionId = "kp_txn_9a8b7c6d"; // From your backend
const snapUrl = `https://app.kitapay.com/snap/${transactionId}`;

window.open(snapUrl, "KitaPaySnap", "width=400,height=650");

// 2. Listen for completion
window.addEventListener("message", (event) => {
  // Always verify origin in production
  if (event.origin !== "https://app.kitapay.com") return;

  try {
    const data = JSON.parse(event.data);
    if (data.type === 'KITAPAY_SNAP_SUCCESS') {
      console.log('Payment Successful!', data.payload);
      // Update your UI to show success state
    }
  } catch(e) {}
});

Webhooks

Webhooks allow you to build or set up integrations which subscribe to certain events on KitaPay. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL.

You must always rely on Webhooks to update the order status in your database, as frontend callbacks (like Snap UI postMessage) can be manipulated or interrupted if the user closes their browser early.

Webhook Payload Example
// POST https://api.yoursite.com/webhook
{
  "event": "payment.success",
  "data": {
    "transaction_id": "kp_txn_9a8b7c6d",
    "external_id": "ORDER-12345",
    "amount": 150000,
    "status": "PAID",
    "paid_at": "2026-08-30T18:05:12Z"
  }
}