API reference
The TujuPay API, field by field.
A predictable REST API over HTTPS. Requests are form-encoded or JSON, responses are always JSON, and every amount is an integer in sen.
◷ Sandbox opens to waitlist developers first. Live keys follow once TujuPay is licensed.
Basics
- Base URL
- https://api.tujupay.com/v1
- Authentication
- HTTP Basic with your secret key as the username
- Amounts
- Integers in sen. RM 189.00 is 18900
- Currency
- myr
- Idempotency
- Send an Idempotency-Key header on every POST. Keys are kept for 24 hours
- Versioning
- Send TujuPay-Version: 2026-10-01 to pin a version
- Pagination
- limit (1 to 100) and starting_after, returning has_more
The Payment object
A Payment represents one attempt by a customer to pay you. It moves through the statuses below.
| Field | Type | Description |
|---|---|---|
| id | string | Unique ID, starting with pay_ |
| status | enum | requires_payment, processing, succeeded, failed, expired or refunded |
| amount | integer | Amount in sen |
| currency | string | Always myr |
| methods | array | Methods offered at checkout, such as fpx and duitnow_qr |
| method_used | string | The method the customer actually paid with |
| reference | string | Your own order reference |
| checkout_url | string | Where to send the customer to pay |
| payout_date | date | The working day this payment is paid out to you |
| created_at | timestamp | When the payment was created, in ISO 8601 |
Payment statuses
- 1
requires_paymentCreated and waiting for the customer to pay - 2
processingThe customer approved and the bank is confirming - 3
succeededPaid. Safe to fulfil the order - 4
failedDeclined by the bank or abandoned by the customer - 5
expiredNot paid within 30 minutes. Create a new payment to try again - 6
refundedFully refunded to the customer
Endpoints
/v1/paymentsCreate a payment
Creates a payment and returns a checkout_url to send the customer to.
Request
curl https://api.tujupay.com/v1/payments \ -u sk_test_51HxQ2...: \ -H "Idempotency-Key: order-2214" \ -d amount=18900 \ -d currency=myr \ -d "methods[]=fpx" \ -d "methods[]=duitnow_qr" \ -d reference=ORDER-2214 \ -d return_url=https://yourshop.my/orders/2214
Response
{
"id": "pay_3Kx9LmQ2",
"status": "requires_payment",
"amount": 18900,
"currency": "myr",
"methods": ["fpx", "duitnow_qr"],
"reference": "ORDER-2214",
"checkout_url": "https://pay.tujupay.com/c/3Kx9LmQ2",
"payout_date": null,
"created_at": "2026-10-09T10:42:00+08:00"
}/v1/payments/{id}Retrieve a payment
Returns the latest state of a payment. Useful as a fallback if you missed a webhook.
Request
curl https://api.tujupay.com/v1/payments/pay_3Kx9LmQ2 \ -u sk_test_51HxQ2...:
Response
{
"id": "pay_3Kx9LmQ2",
"status": "succeeded",
"amount": 18900,
"method_used": "fpx",
"payout_date": "2026-10-09"
}/v1/refundsRefund a payment
Refunds all or part of a succeeded payment. Leave out amount to refund in full.
Request
curl https://api.tujupay.com/v1/refunds \ -u sk_test_51HxQ2...: \ -H "Idempotency-Key: refund-2214-1" \ -d payment=pay_3Kx9LmQ2 \ -d amount=5000
Response
{
"id": "re_7Pq1Xs",
"payment": "pay_3Kx9LmQ2",
"amount": 5000,
"status": "processing"
}/v1/payment_linksCreate a payment link
Creates a shareable link for selling in chat or on social media.
Request
curl https://api.tujupay.com/v1/payment_links \ -u sk_test_51HxQ2...: \ -d amount=6500 \ -d "description=Kek Lapis Sarawak, 1 box" \ -d single_use=true
Response
{
"id": "plink_9Ad2",
"url": "https://pay.tujupay.com/l/kek-lapis",
"amount": 6500,
"single_use": true,
"active": true
}/v1/payoutsList payouts
Lists payouts to your bank, newest first, with a statement for each.
Request
curl "https://api.tujupay.com/v1/payouts?limit=2" \ -u sk_test_51HxQ2...:
Response
{
"data": [
{ "id": "po_1Tz", "amount": 320760, "status": "paid", "arrival_date": "2026-10-09" },
{ "id": "po_0Ym", "amount": 291840, "status": "paid", "arrival_date": "2026-10-08" }
],
"has_more": true
}Errors
Errors return a standard HTTP status and a JSON body with a type, a code and a message written for humans.
| Status | Code | What it means |
|---|---|---|
| 400 | invalid_request | A parameter is missing or wrong. The message says which |
| 401 | unauthorized | The API key is missing, wrong or revoked |
| 404 | not_found | No object with that ID in this mode |
| 409 | idempotency_conflict | The same Idempotency-Key was used with different parameters |
| 429 | rate_limited | Too many requests. Wait and retry with backoff |
| 500 | server_error | Something went wrong on our side. Safe to retry with the same key |
{
"error": {
"type": "invalid_request",
"code": "amount_too_small",
"message": "amount must be at least 100 sen (RM 1.00).",
"param": "amount"
}
}