Skip to content

API documentation

A REST API for creating QRIS transactions, checking their status, and receiving payment notifications.

Introduction

The Tatsupay API is REST-based. Requests and responses use JSON, and every request is authenticated with an API key via the Authorization header. No SDK is required.

Base URL

All endpoints live under the base URL below. Every request must go over HTTPS.

base url
https://api.tatsupay.com

Authentication

Pass your API key as a bearer token in the Authorization header. Test keys are prefixed sk_test_ and live keys sk_live_.

header
Authorization: Bearer sk_test_your_key_here

An API key grants full access to your account's transactions. Keep it in a server-side environment variable, and never ship it in code that reaches a browser or mobile app.

Test and live mode

Every account has two separate sets of keys. Test-mode transactions involve no real funds and can be settled through the simulate endpoint, so the whole integration can be exercised first.

Create a transaction

POST/v1/transactions

Creates a new transaction and returns QRIS data ready to display to your customer.

Parameters

  • amount — integer, amount in whole rupiah. Required.
  • payment_method — payment method code, for example qris. Required.
  • reference — your own order identifier. Optional, but strongly recommended for reconciliation.
curl
curl -X POST "https://api.tatsupay.com/v1/transactions" \
  -H "Authorization: Bearer sk_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "payment_method": "qris",
    "reference": "ORDER-1042"
  }'
200 response
{
  "id": "trx_01HQ8Z3XK2M9V4N7P1R5T8W2Y6",
  "status": "pending",
  "amount": 150000,
  "payment_method": "qris",
  "reference": "ORDER-1042",
  "qr_string": "00020101021226...",
  "url_image_qris": "https://api.tatsupay.com/v1/transactions/trx_01HQ8Z3XK2M9V4N7P1R5T8W2Y6/qris.png",
  "expires_at": "2026-10-03T01:15:00Z",
  "created_at": "2026-10-03T00:45:00Z"
}
node.js
const res = await fetch('https://api.tatsupay.com/v1/transactions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TATSUPAY_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 150000,
    payment_method: 'qris',
    reference: 'ORDER-1042',
  }),
});

const trx = await res.json();

Check status

GET/v1/transactions/{id}

Retrieves the current state of a transaction. Useful as a fallback check if a webhook does not arrive.

curl
curl "https://api.tatsupay.com/v1/transactions/trx_01HQ8Z3XK2M9V4N7P1R5T8W2Y6" \
  -H "Authorization: Bearer sk_test_your_key"

Webhooks

When a transaction settles, Tatsupay sends a POST request to the endpoint you register in the dashboard. Your endpoint must respond with a 2xx status; otherwise delivery is retried with increasing delays.

payload
{
  "event": "transaction.paid",
  "data": {
    "id": "trx_01HQ8Z3XK2M9V4N7P1R5T8W2Y6",
    "status": "paid",
    "amount": 150000,
    "reference": "ORDER-1042",
    "paid_at": "2026-10-03T00:47:12Z"
  }
}

Your webhook endpoint must be idempotent. Retries can deliver the same event more than once, and processing an order twice is a far more damaging outcome than re-checking an order's status before acting on it.

Transaction statuses

  • pending — transaction created, awaiting payment.
  • paid — payment received and the transaction has settled.
  • expired — the payment window elapsed.
  • failed — the transaction failed and cannot be paid.

Error handling

Failures return an appropriate HTTP status code along with a JSON body carrying an error code. Branch on the code rather than the message text: messages may be reworded over time, codes will not.

error
{
  "error": {
    "code": "VALIDATION_ERROR",
    "field": "amount"
  }
}

Rate limits

Requests are rate limited per API key. When the limit is exceeded you receive a 429 along with a Retry-After header stating how many seconds to wait before retrying.

For machines and AI agents

An OpenAPI specification and a plain-text summary are available at the addresses below, so automated tools do not need to parse this page's HTML.