Referensi API
API merchant RAGH Reader dipakai server Anda untuk membuat tagihan dan payout, membaca statusnya, dan mengelola webhook. Halaman ini memuat semua endpoint, field, batas validasi, dan kode error.
Spesifikasi mesin (OpenAPI 3.1) tersedia di
/docs/openapi.yaml. File itu bisa diimpor ke Postman,
Insomnia, atau generator klien.
Dasar
| Base URL | https://payment-reader.ragh.co.id |
| Prefix endpoint | /api/v1 |
| Format | JSON UTF-8. Kirim Content-Type: application/json |
| Uang | Integer rupiah, tanpa desimal. Rp 200.123 ditulis 200123 |
| Waktu | ISO-8601 dengan zona WIB, misalnya 2026-09-28T14:05:00+07:00 |
| Versi API | 2026-09-28 (juga tercantum di setiap webhook sebagai api_version) |
Autentikasi
Setiap request membawa API key di header Authorization:
Authorization: Bearer rk_live_AbCdEf0123456789AbCdEf0123456789AbCdEf01
- Kunci dibuat per aplikasi di dashboard. Pembuatannya meminta OTP email.
- Bentuk kunci:
rk_live_ataurk_test_, diikuti 40 huruf dan angka. - Kunci hanya ditampilkan sekali saat dibuat. Server kami hanya menyimpan hash-nya.
- Simpan kunci di server Anda (variabel lingkungan atau secret manager). Jangan menaruhnya di app HP, kode frontend, atau repo git.
- Dashboard menampilkan kapan setiap kunci terakhir dipakai dan dari IP mana. Cabut kunci yang tidak dipakai atau dicurigai bocor.
Kunci yang salah bentuk, tidak dikenal, atau sudah dicabut ditolak dengan
401 invalid_api_key.
Kunci uji dan kunci live
| Prefix | Mode | Keterangan |
|---|---|---|
rk_test_ |
Uji | Hanya membuat dan membaca data uji (test: true). Tidak pernah cocok dengan transfer sungguhan. Tidak ditagih. Bisa memakai simulasi |
rk_live_ |
Live | Data sungguhan. Butuh verifikasi identitas (KYC) |
Setiap endpoint hanya melihat data dengan mode yang sama dengan kuncinya. Tagihan uji tidak bisa dibaca dengan kunci live, dan sebaliknya. Rinciannya di Konsep: mode uji.
Scope
Setiap kunci punya daftar scope. Request ke endpoint di luar scope kunci
ditolak dengan 403 insufficient_scope.
| Scope | Endpoint |
|---|---|
payments:read |
GET /payments, GET /payments/{id} |
payments:write |
POST /payments, POST /payments/{id}/extend, POST /payments/{id}/cancel, POST /payments/{id}/mark-paid |
payouts:read |
GET /payouts, GET /payouts/{id} |
payouts:write |
POST /payouts, POST /payouts/{id}/extend, POST /payouts/{id}/cancel, POST /payouts/{id}/mark-sent |
mutations:read |
GET /mutations |
webhooks:manage |
GET /webhook-events, POST /webhook-events/{id}/resend |
| (tanpa scope khusus) | GET /bank-accounts, POST /test/simulate (khusus kunci uji) |
Berikan scope seminimal mungkin. Contoh: server toko cukup payments:read dan
payments:write.
Allowlist IP
Kunci bisa dibatasi ke daftar IP atau rentang CIDR (IPv4 dan IPv6), misalnya
203.0.113.10, 198.51.100.0/24. Request dari IP lain ditolak dengan
403 ip_not_allowed.
Kunci live dengan scope payouts:write wajib memakai allowlist IP. Kunci
itu bisa membuat instruksi "transfer ke rekening X". Kalau bocor tanpa
batasan, penyerang bisa menyisipkan instruksi transfer ke rekeningnya sendiri.
Pakai kunci terpisah khusus payout, dan batasi ke IP server yang membuat
payout. Aturan ini juga diperiksa di setiap request: kunci live
payouts:write tanpa allowlist ditolak dengan 403 ip_allowlist_required.
Batas kecepatan
Paling banyak 300 request per menit per API key. Kelebihannya ditolak
dengan 429 rate_limited. Tunggu sekitar satu menit, lalu coba lagi.
Jangan memakai polling cepat untuk menunggu status tagihan. Pakai webhook, dan
pakai GET hanya untuk rekonsiliasi atau cadangan.
Format error
Semua error API memakai bentuk yang sama:
{
"error": {
"code": "validation_failed",
"message": "Ada isian yang tidak valid.",
"fields": {
"amount": ["The amount field must be at least 1000."]
}
}
}
codestabil dan aman dipakai di kode Anda.messageberbahasa Indonesia untuk dibaca manusia. Isinya bisa berubah.- Beberapa error membawa data tambahan:
fields(validasi),payment, ataupayout(lihat tabel).
Daftar kode error
| HTTP | code |
Arti dan tindakan |
|---|---|---|
| 401 | invalid_api_key |
Header Authorization tidak ada, bentuk kunci salah, kunci tidak dikenal, kunci sudah dicabut, atau aplikasinya nonaktif |
| 402 | balance_low |
Saldo Kredit Layanan di bawah batas minus. Tagihan dan payout baru ditolak, yang sudah ada tetap diproses. Top-up dulu |
| 402 | subscription_expired |
Langganan berakhir dan tidak diperpanjang. Tagihan dan payout baru ditolak sampai langganan dibeli lagi |
| 403 | ip_not_allowed |
IP server Anda tidak ada di allowlist kunci |
| 403 | ip_allowlist_required |
Kunci live dengan scope payouts:write belum punya allowlist IP. Buat kunci baru dengan allowlist |
| 403 | insufficient_scope |
Kunci tidak punya scope untuk endpoint ini |
| 403 | account_suspended |
Layanan akun ditangguhkan admin. Tagihan dan payout baru ditolak, yang sudah ada tetap diproses |
| 403 | account_terminated |
Layanan akun diputus. Karena semua kunci ikut dicabut saat pemutusan, yang biasanya terlihat justru 401 invalid_api_key |
| 403 | kyc_required |
Kunci live dipakai sebelum verifikasi identitas selesai. Kunci uji tetap bisa dipakai |
| 403 | volume_cap_reached |
Akun perorangan melewati batas volume Rp 50 juta per bulan. Ajukan verifikasi badan usaha |
| 403 | test_key_required |
POST /test/simulate dipanggil dengan kunci live |
| 404 | not_found |
ID tidak ada, milik aplikasi lain, atau beda mode (uji vs live) |
| 409 | order_already_paid |
reference_id ini sudah lunas. error.payment berisi tagihan yang lunas. Jangan tagih lagi |
| 409 | payout_already_sent |
reference_id ini sudah terkirim. error.payout berisi payout yang terkirim. Jangan transfer lagi |
| 409 | payout_not_enabled |
Belum ada rekening dengan payout aktif, atau bank_code yang diminta belum aktif payout-nya |
| 409 | unique_code_exhausted |
Semua 999 kode unik untuk nominal ini di rekening itu sedang terkunci. Coba lagi nanti, ubah nominal, atau perpendek masa berlaku |
| 409 | not_extendable |
Tagihan atau payout tidak bisa diperpanjang: sudah selesai, dibatalkan, atau masa jedanya lewat. Buat yang baru |
| 409 | not_cancellable |
Hanya catatan berstatus pending yang bisa dibatalkan |
| 422 | validation_failed |
Isian tidak valid. error.fields berisi pesan per field. Juga dipakai bila bank_code bukan rekening aktif Anda |
| 422 | no_bank_account |
Kunci live dipakai sebelum ada rekening terverifikasi. Tambahkan rekening dari app RAGH Reader |
| 429 | rate_limited |
Lebih dari 300 request per menit |
| 429 | resend_cooldown |
Event yang sama sudah dikirim ulang manual kurang dari 60 detik lalu |
| lain | http_<status> |
Error HTTP umum, misalnya http_405 untuk metode yang salah |
Error 5xx dari server kami tidak selalu memakai format di atas. Aman untuk
mengulang POST /payments dan POST /payouts dengan reference_id yang sama
(lihat Aturan pemanggilan ulang).
ID dan format data
| Awalan | Objek | Contoh |
|---|---|---|
pay_ |
Tagihan | pay_01j8zq5v3m7c2k9x4h6t1r0bna |
po_ |
Payout | po_01j8zr1c2d3e4f5g6h7j8k9m0n |
mut_ |
Mutasi | mut_01j8zqb7r2s3t4v5w6x7y8z9a0 |
evt_ |
Event webhook | evt_01j8zqa2b3c4d5e6f7g8h9j0km |
ID berupa awalan + 26 karakter huruf kecil dan angka. Perlakukan ID sebagai string utuh dengan panjang maksimal 40 karakter.
Nomor transaksi (tx_number) berbentuk <PREFIX>-<YYMMDD>-<urutan>, misalnya
TKO-260928-0042. Lihat Konsep.
Ringkasan endpoint
| Metode | Path | Scope |
|---|---|---|
| POST | /api/v1/payments |
payments:write |
| GET | /api/v1/payments?reference_id=… |
payments:read |
| GET | /api/v1/payments/{id} |
payments:read |
| POST | /api/v1/payments/{id}/extend |
payments:write |
| POST | /api/v1/payments/{id}/cancel |
payments:write |
| POST | /api/v1/payments/{id}/mark-paid |
payments:write |
| POST | /api/v1/payouts |
payouts:write |
| GET | /api/v1/payouts?reference_id=… |
payouts:read |
| GET | /api/v1/payouts/{id} |
payouts:read |
| POST | /api/v1/payouts/{id}/extend |
payouts:write |
| POST | /api/v1/payouts/{id}/cancel |
payouts:write |
| POST | /api/v1/payouts/{id}/mark-sent |
payouts:write |
| GET | /api/v1/mutations |
mutations:read |
| GET | /api/v1/bank-accounts |
— |
| GET | /api/v1/webhook-events |
webhooks:manage |
| POST | /api/v1/webhook-events/{id}/resend |
webhooks:manage |
| POST | /api/v1/test/simulate |
— (kunci uji saja) |
Tagihan
POST /api/v1/payments
Membuat tagihan (uang masuk). Balasan 201 untuk tagihan baru, atau 200
bila tagihan yang sudah ada dipakai lagi (reused: true).
Body:
| Field | Tipe | Wajib | Batas | Keterangan |
|---|---|---|---|---|
reference_id |
string | Ya | maks 128 | ID order di sistem Anda. Satu order = satu reference_id, sehingga memanggil ulang selalu aman (lihat di bawah) |
amount |
integer | Ya | 1.000 – 250.000.000 | Nominal dasar, sebelum kode unik |
bank_code |
string | Tidak | maks 16 | Rekening tujuan: BCA, MANDIRI, BRI, BNI, GOPAY, SHOPEEPAY. Kunci uji juga menerima UJI. Tanpa field ini, dipilih rekening utama yang terpantau |
expires_in_minutes |
integer | Tidak | 15 – 10.080 | Masa berlaku. Bawaan 1.440 (24 jam) |
description |
string | Tidak | maks 255 | Keterangan tagihan |
customer |
object | Tidak | Data pelanggan untuk analitik "top pelanggan" | |
customer.ref |
string | Tidak | maks 128 | ID pelanggan di sistem Anda |
customer.name |
string | Tidak | maks 190 | Nama pelanggan |
customer.contact |
string | Tidak | maks 190 | Email atau nomor HP |
tags |
array of string | Tidak | maks 20 item, masing-masing maks 64 | Misalnya produk:kursus-a, kanal:instagram, untuk analitik "top sumber" |
return_url |
string (URL) | Tidak | maks 255, hanya http:// atau https:// |
Alamat tujuan pembeli setelah membayar |
metadata |
object | Tidak | JSON maks 4 KB | Data bebas milik Anda. Dikembalikan apa adanya di API dan webhook |
Mode uji atau live ditentukan oleh kunci, bukan oleh field di body.
Pemilihan rekening bila bank_code tidak dikirim:
- rekening utama, bila terpantau;
- rekening lain yang terpantau;
- rekening utama, meskipun tidak terpantau (tagihan dibuat dengan
confirmation_delayed: true).
Bila bank_code dikirim tetapi rekening itu tidak aktif atau belum
terverifikasi, balasannya 422 validation_failed dengan
error.fields.bank_code.
Contoh request:
curl -X POST https://payment-reader.ragh.co.id/api/v1/payments \
-H "Authorization: Bearer rk_live_…" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "ORDER-123",
"amount": 200000,
"bank_code": "BCA",
"expires_in_minutes": 1440,
"description": "Kursus A",
"customer": { "ref": "CUST-9", "name": "Budi", "contact": "081234567890" },
"tags": ["produk:kursus-a", "kanal:instagram"],
"return_url": "https://toko.contoh.id/order/123",
"metadata": { "cart_id": "C-77" }
}'
Contoh balasan 201:
{
"id": "pay_01j8zq5v3m7c2k9x4h6t1r0bna",
"tx_number": "TKO-260928-0042",
"reference_id": "ORDER-123",
"amount": 200000,
"unique_code": 123,
"total_amount": 200123,
"bank": {
"code": "BCA",
"name": "BCA",
"account_number": "1234567890",
"account_holder": "PT CONTOH JAYA"
},
"status": "pending",
"confirmation_delayed": false,
"late": false,
"paid_after_cancel": false,
"match_type": null,
"test": false,
"description": "Kursus A",
"customer": { "ref": "CUST-9", "name": "Budi", "contact": "081234567890" },
"tags": ["produk:kursus-a", "kanal:instagram"],
"metadata": { "cart_id": "C-77" },
"expires_at": "2026-09-29T10:15:00+07:00",
"paid_at": null,
"expired_at": null,
"cancelled_at": null,
"created_at": "2026-09-28T10:15:00+07:00",
"payment_url": "https://payment-reader.ragh.co.id/pay/pay_01j8zq5v3m7c2k9x4h6t1r0bna",
"reused": false
}
Tampilkan ke pembeli: total_amount (bukan amount), bank, dan batas
waktu. Atau cukup arahkan pembeli ke payment_url.
Error yang mungkin: 402 balance_low, 402 subscription_expired,
403 account_suspended, 403 account_terminated, 403 kyc_required,
403 volume_cap_reached, 409 order_already_paid,
409 unique_code_exhausted, 422 validation_failed, 422 no_bank_account.
Aturan pemanggilan ulang
Memanggil POST /api/v1/payments berulang kali dengan reference_id yang
sama selalu aman. Ulangi saja bila koneksi putus atau server Anda tidak
yakin apakah request sebelumnya berhasil.
reference_id unik per aplikasi dan per mode (uji/live). Hasilnya
bergantung pada tagihan yang sudah ada:
Keadaan tagihan lama dengan reference_id sama |
Hasil | HTTP |
|---|---|---|
| Ada yang sudah lunas | Ditolak order_already_paid. error.payment berisi tagihan yang lunas |
409 |
Pending, amount sama |
Tagihan yang sama dikembalikan, reused: true |
200 |
Kedaluwarsa, masih dalam masa jeda, amount sama |
Tagihan yang sama dibuka lagi: status pending, batas waktu baru, kode dan total tetap, reused: true |
200 |
Pending atau kedaluwarsa-dalam-jeda, amount berbeda |
Tagihan lama dibatalkan (totalnya tetap terkunci 48 jam), lalu tagihan baru dibuat | 201 |
| Tidak ada, sudah dibatalkan, atau masa jedanya sudah lewat | Tagihan baru dibuat | 201 |
Aturan yang sama berlaku untuk payout, dengan payout_already_sent sebagai
pengganti order_already_paid.
Bila satu tagihan untuk sebuah reference_id lunas, tagihan lain yang masih
pending untuk reference_id yang sama otomatis dibatalkan.
Saat tagihan lama dipakai lagi (reused: true), field lain di body
(bank_code, description, dan seterusnya) diabaikan. Satu-satunya
pengecualian: expires_in_minutes dipakai sebagai batas baru saat tagihan
kedaluwarsa dibuka lagi.
GET /api/v1/payments/{id}
Membaca satu tagihan. Balasannya objek tagihan seperti di atas, tanpa field
reused.
curl https://payment-reader.ragh.co.id/api/v1/payments/pay_01j8zq5v3m7c2k9x4h6t1r0bna \
-H "Authorization: Bearer rk_live_…"
Tagihan milik aplikasi lain atau beda mode menghasilkan 404 not_found.
GET /api/v1/payments?reference_id=…
Semua tagihan untuk satu reference_id, terbaru dulu, paling banyak 50.
Berguna untuk melihat riwayat satu order (misalnya tagihan lama yang
dibatalkan karena nominal berubah).
| Query | Wajib | Batas |
|---|---|---|
reference_id |
Ya | maks 128 |
{
"data": [
{ "id": "pay_01j8zq5v3m7c2k9x4h6t1r0bna", "reference_id": "ORDER-123", "status": "paid", "…": "…" },
{ "id": "pay_01j8zp0a1b2c3d4e5f6g7h8j9k", "reference_id": "ORDER-123", "status": "cancelled", "…": "…" }
]
}
POST /api/v1/payments/{id}/extend
Memperpanjang tagihan yang sama. Kode unik dan total tetap. Ini cara yang benar untuk "bayar lagi" (lihat Konsep).
| Field | Wajib | Batas | Keterangan |
|---|---|---|---|
expires_in_minutes |
Tidak | 15 – 10.080 | Batas baru dihitung dari sekarang. Bawaan 1.440 |
- Bisa untuk tagihan
pending, atauexpiredyang masih dalam masa jeda. - Selain itu:
409 not_extendable. - Balasan: objek tagihan terbaru.
POST /api/v1/payments/{id}/cancel
Membatalkan tagihan pending. Body kosong.
- Totalnya tetap terkunci 48 jam. Bila pembeli tetap mentransfer dalam
masa itu, tagihan menjadi lunas dengan
late: truedanpaid_after_cancel: true, lalu masuk daftar perlu tindakan. - Tagihan yang bukan
pending:409 not_cancellable. - Balasan: objek tagihan terbaru.
POST /api/v1/payments/{id}/mark-paid
Menandai lunas dari sistem Anda, misalnya admin sudah melihat uangnya di mutasi bank sendiri. Body kosong. Sama dengan tombol Tandai lunas di dashboard:
- Tagihan menjadi
paiddenganmatch_type: "manual_unverified", dan webhookpayment.paidtetap dikirim. - Bila notifikasi bank untuk total itu datang belakangan, tagihan menjadi
terverifikasi (
match_type: "manual") dan Anda menerimapayment.verified, bukan uang dobel. - Tidak terverifikasi dalam 24 jam → masuk daftar perlu tindakan.
- Sudah lunas:
409 already_paid.
Objek tagihan
| Field | Tipe | Keterangan |
|---|---|---|
id |
string | ID tagihan (pay_…) |
tx_number |
string | Nomor transaksi berprefix |
reference_id |
string | null | ID order Anda |
amount |
integer | Nominal dasar |
unique_code |
integer | Kode unik 1–999 |
total_amount |
integer | amount + unique_code. Yang harus ditransfer |
bank |
object | Rekening tujuan: code, name, account_number, account_holder |
status |
string | pending, paid, expired, cancelled |
confirmation_delayed |
boolean | true selama rekening tidak terpantau. Batas bayar ditahan |
late |
boolean | Lunas setelah kedaluwarsa, dalam masa jeda |
paid_after_cancel |
boolean | Lunas setelah dibatalkan |
match_type |
string | null | auto, manual, manual_unverified, atau null bila belum lunas |
test |
boolean | Data mode uji |
description |
string | null | |
customer |
object | null | ref, name, contact |
tags |
array | Selalu array, bisa kosong |
metadata |
object | Selalu objek, bisa kosong |
expires_at |
string | Batas bayar awal. Bisa lewat sementara status masih pending karena penahanan |
paid_at |
string | null | |
expired_at |
string | null | |
cancelled_at |
string | null | |
created_at |
string | |
payment_url |
string | Halaman bayar siap pakai |
Payout
Payout mencatat transfer keluar yang akan Anda lakukan sendiri. Reader membuktikannya dari notifikasi uang keluar. Payout aktif per rekening setelah transfer uji keluar di app RAGH Reader (lihat Pasang RAGH Reader).
POST /api/v1/payouts
Body:
| Field | Tipe | Wajib | Batas | Keterangan |
|---|---|---|---|---|
reference_id |
string | Ya | maks 128 | ID pencairan di sistem Anda. Aturannya sama dengan tagihan |
amount |
integer | Ya | 1.000 – 250.000.000 | Nominal hak penerima, sebelum kode unik |
bank_code |
string | Tidak | maks 16 | Rekening sumber. Harus rekening dengan payout aktif. Tanpa field ini, dipilih rekening utama yang terpantau |
recipient |
object | Ya | Penerima | |
recipient.bank_code |
string | Ya | maks 16 | Bank penerima, misalnya BCA |
recipient.account_number |
string | Ya | maks 64 | Nomor rekening penerima |
recipient.account_name |
string | Ya | maks 190 | Nama pemilik rekening penerima |
expires_in_minutes |
integer | Tidak | 15 – 10.080 | Bawaan 1.440 |
description |
string | Tidak | maks 255 | |
tags |
array of string | Tidak | maks 20 item, masing-masing maks 64 | |
metadata |
object | Tidak | JSON maks 4 KB |
Contoh request:
curl -X POST https://payment-reader.ragh.co.id/api/v1/payouts \
-H "Authorization: Bearer rk_live_…" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "WD-456",
"amount": 150000,
"recipient": {
"bank_code": "BCA",
"account_number": "9876543210",
"account_name": "BUDI SANTOSO"
},
"description": "Pencairan komisi September"
}'
Contoh balasan 201:
{
"id": "po_01j8zr1c2d3e4f5g6h7j8k9m0n",
"tx_number": "TKO-260928-0043",
"reference_id": "WD-456",
"amount": 150000,
"unique_code": 123,
"total_amount": 150123,
"source_bank": {
"code": "MANDIRI",
"name": "Livin' by Mandiri",
"account_number": "1230000123456",
"account_holder": "PT CONTOH JAYA"
},
"recipient": {
"bank_code": "BCA",
"account_number": "9876543210",
"account_name": "BUDI SANTOSO"
},
"status": "pending",
"confirmation_delayed": false,
"late": false,
"sent_after_cancel": false,
"recipient_check": null,
"match_type": null,
"test": false,
"description": "Pencairan komisi September",
"tags": [],
"metadata": {},
"instruction": "Transfer tepat Rp 150.123 ke BCA 9876543210 a.n. BUDI SANTOSO",
"expires_at": "2026-09-29T10:20:00+07:00",
"sent_at": null,
"expired_at": null,
"cancelled_at": null,
"reversed_at": null,
"created_at": "2026-09-28T10:20:00+07:00",
"reused": false
}
Tampilkan instruction ke admin yang mentransfer. Admin harus:
- mentransfer tepat
total_amountdari rekeningsource_bank; - memakai app bank di HP yang dibaca Reader;
- melakukan satu transfer per payout (bukan transfer massal).
Error yang mungkin: 402 balance_low, 402 subscription_expired,
403 account_suspended, 403 account_terminated, 403 kyc_required,
409 payout_already_sent, 409 payout_not_enabled,
409 unique_code_exhausted, 422 validation_failed.
GET /api/v1/payouts/{id}
Membaca satu payout. Balasannya objek payout tanpa reused.
GET /api/v1/payouts?reference_id=…
Semua payout untuk satu reference_id, terbaru dulu, paling banyak 50.
Balasan: { "data": [ … ] }.
POST /api/v1/payouts/{id}/extend
Sama dengan perpanjangan tagihan. Body opsional expires_in_minutes
(15 – 10.080). Kode dan total tetap. Selain pending atau expired dalam
masa jeda: 409 not_extendable.
POST /api/v1/payouts/{id}/cancel
Membatalkan payout pending. Totalnya tetap terkunci 48 jam. Bila transfer
tetap terjadi dalam masa itu, payout menjadi sent dengan late: true dan
sent_after_cancel: true, lalu masuk perlu tindakan. Selain pending:
409 not_cancellable.
POST /api/v1/payouts/{id}/mark-sent
Menandai terkirim dari sistem Anda (cadangan bila notifikasi uang keluar tidak
terbaca). Body kosong. Payout menjadi sent dengan
match_type: "manual_unverified"; notifikasi uang keluar yang datang
belakangan memverifikasinya (payout.verified). Sudah terkirim:
409 already_sent.
Objek payout
| Field | Tipe | Keterangan |
|---|---|---|
id |
string | ID payout (po_…) |
tx_number |
string | Nomor transaksi berprefix |
reference_id |
string | null | |
amount |
integer | Nominal hak penerima |
unique_code |
integer | Kode unik 1–999, ditambahkan |
total_amount |
integer | Yang harus ditransfer |
source_bank |
object | Rekening sumber: code, name, account_number, account_holder |
recipient |
object | bank_code, account_number, account_name |
status |
string | pending, sent, expired, cancelled, reversed |
confirmation_delayed |
boolean | true selama rekening sumber tidak terpantau |
late |
boolean | Terkirim setelah kedaluwarsa, dalam masa jeda |
sent_after_cancel |
boolean | Terkirim setelah dibatalkan |
recipient_check |
string | null | match, mismatch, unavailable, atau null bila belum terkirim |
match_type |
string | null | auto, manual, manual_unverified |
test |
boolean | |
description, tags, metadata |
Seperti tagihan | |
instruction |
string | Kalimat instruksi transfer siap tampil |
expires_at, sent_at, expired_at, cancelled_at, reversed_at, created_at |
string | null |
Mutasi
GET /api/v1/mutations
Semua mutasi (notifikasi transaksi) yang tercatat di seluruh rekening akun
Anda, terbaru dulu. Berguna untuk pembukuan dan rekonsiliasi. Mutasi
ignored tidak ditampilkan.
| Query | Batas | Keterangan |
|---|---|---|
direction |
credit atau debit |
Masuk atau keluar |
match_status |
maks 24 karakter | Misalnya no_candidate |
from |
tanggal | Waktu diterima server ≥ nilai ini |
to |
tanggal | Waktu diterima server ≤ nilai ini |
limit |
1 – 200 | Bawaan 100 |
Tulis from dan to dalam WIB, dengan format 2026-09-28 atau
2026-09-28 14:00:00. Tanggal tanpa jam berarti pukul 00.00, jadi untuk
"sampai akhir 28 September" pakai to=2026-09-28 23:59:59.
{
"data": [
{
"id": "mut_01j8zqb7r2s3t4v5w6x7y8z9a0",
"bank": "BCA",
"direction": "credit",
"kind": "transfer_in",
"amount": 200123,
"fee_amount": null,
"counterparty_name": "BUDI SANTOSO",
"match_status": "matched",
"is_match": true,
"payment_id": "pay_01j8zq5v3m7c2k9x4h6t1r0bna",
"payout_id": null,
"transaction_at": "2026-09-28T10:31:00+07:00",
"received_at": "2026-09-28T10:31:04+07:00",
"test": false
}
]
}
| Field | Keterangan |
|---|---|
bank |
Kode bank rekening Anda (string) |
direction |
credit (masuk) atau debit (keluar) |
kind |
transfer_in, qris_in, transfer_out, purchase, fee, reversal, interest |
fee_amount |
Biaya transfer bila tertulis terpisah di notifikasi |
counterparty_name |
Nama pengirim (masuk) atau penerima (keluar) dari notifikasi. Bisa kosong atau terpotong |
match_status |
Lihat Konsep: status mutasi |
transaction_at |
Jam transaksi dari teks notifikasi, bila bank menyertakannya |
received_at |
Jam server menerima notifikasi |
Rekening
GET /api/v1/bank-accounts
Rekening terverifikasi milik akun Anda, beserta status pemantauan dan payout. Bisa dipanggil dengan kunci apa pun.
{
"data": [
{
"code": "BCA",
"name": "BCA",
"account_number": "1234567890",
"account_holder": "PT CONTOH JAYA",
"kind": "bank",
"is_default": true,
"monitored": true,
"payout_enabled": false
}
]
}
| Field | Keterangan |
|---|---|
kind |
bank atau ewallet |
is_default |
Rekening utama untuk tagihan tanpa bank_code |
monitored |
true bila rekening sedang terpantau |
payout_enabled |
true bila transfer uji keluar sudah berhasil |
Event webhook
GET /api/v1/webhook-events
Event webhook aplikasi ini beserta percobaan kirim terakhirnya, terbaru dulu.
Kunci uji hanya melihat event uji dan kunci live hanya melihat event live;
event ping terlihat di keduanya. Aturan yang sama berlaku untuk
POST /webhook-events/{id}/resend.
| Query | Batas | Keterangan |
|---|---|---|
status |
pending, delivered, rejected, failed |
|
from |
tanggal | Waktu event dibuat ≥ nilai ini (WIB) |
limit |
1 – 200 | Bawaan 50 |
{
"data": [
{
"id": "evt_01j8zqa2b3c4d5e6f7g8h9j0km",
"type": "payment.paid",
"status": "pending",
"attempts": 3,
"last_error_code": "INVALID_ACK",
"next_attempt_at": "2026-09-28T10:52:05+07:00",
"delivered_at": null,
"created_at": "2026-09-28T10:31:05+07:00",
"last_delivery": {
"status_code": 200,
"error_code": "INVALID_ACK",
"ack_status": "invalid",
"duration_ms": 184,
"at": "2026-09-28T10:37:05+07:00"
}
}
]
}
status event |
Arti |
|---|---|
pending |
Belum berhasil. Masih dalam jadwal kirim ulang, atau pengiriman sedang dijeda/dinonaktifkan |
delivered |
Diterima sistem Anda dengan ack accepted atau duplicate |
rejected |
Sistem Anda membalas ack rejected. Tidak dikirim ulang otomatis |
failed |
Tidak berhasil dalam 72 jam. Masih bisa dikirim ulang manual |
Arti error_code ada di Webhook: pemetaan respons.
POST /api/v1/webhook-events/{id}/resend
Mengirim ulang satu event seketika, sama dengan tombol "Kirim ulang sekarang" di dashboard.
- Berlaku untuk event berapa pun umurnya, termasuk yang sudah
failedataurejected. - Melewati jadwal otomatis, pemutus sirkuit, dan jeda pengiriman.
- Paling banyak 1 kali per 60 detik per event. Lebih cepat dari itu:
429 resend_cooldown. - Balasan selalu
200dengan objek event terbaru. Periksalast_delivery.error_codeuntuk tahu hasilnya:nullberarti berhasil. - Bila gagal, jadwal otomatis event itu tetap berjalan. Batas 72 jam tidak diperpanjang.
Kirim ulang massal ("semua yang gagal dalam 30 menit terakhir") hanya tersedia di dashboard. Lihat Webhook.
Mode uji
POST /api/v1/test/simulate
Memalsukan notifikasi bank untuk tagihan atau payout uji, supaya Anda bisa
menguji semua cabang webhook tanpa transfer sungguhan. Hanya bisa dengan kunci
rk_test_. Kunci live: 403 test_key_required.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
payment_id |
string | Salah satu | Tagihan uji yang disimulasikan (uang masuk) |
payout_id |
string | Salah satu | Payout uji yang disimulasikan (uang keluar) |
amount |
integer | Tidak | Nominal yang "ditransfer", minimal 1. Bawaan: total_amount |
counterparty_name |
string | Tidak | Maks 190. Nama pengirim (tagihan) atau penerima (payout). Bawaan: PEMBELI SIMULASI untuk tagihan, nama penerima untuk payout |
expire_first |
boolean | Tidak | Bila true dan catatannya pending, catatan dibuat kedaluwarsa dulu, sehingga hasilnya telat (late: true) |
Balasan 200:
{
"mutation": {
"id": "mut_01j8zqb7r2s3t4v5w6x7y8z9a0",
"bank": "UJI",
"direction": "credit",
"kind": "transfer_in",
"amount": 150123,
"fee_amount": null,
"counterparty_name": "PEMBELI SIMULASI",
"match_status": "matched",
"is_match": true,
"payment_id": "pay_01j8zq5v3m7c2k9x4h6t1r0bna",
"payout_id": null,
"transaction_at": null,
"received_at": "2026-09-28T10:31:04+07:00",
"test": true
},
"payment": { "id": "pay_01j8zq5v3m7c2k9x4h6t1r0bna", "status": "paid", "late": false, "test": true, "…": "…" }
}
Untuk payout, kunci kedua bernama payout, bukan payment.
Skenario yang bisa diuji:
| Skenario | Cara | Webhook |
|---|---|---|
| Lunas | {"payment_id": "…"} |
payment.paid |
| Lunas telat | {"payment_id": "…", "expire_first": true} |
payment.paid dengan late: true |
| Lunas setelah dibatalkan | Batalkan dulu, lalu simulasikan | payment.paid dengan paid_after_cancel: true |
| Uang dobel | Simulasikan dua kali untuk tagihan yang sama | payment.duplicate |
| Transfer tanpa pasangan | amount berbeda dari total |
mutation.unmatched, bila diaktifkan di pengaturan aplikasi |
| Payout terkirim | {"payout_id": "…"} |
payout.sent dengan recipient_check: "match" |
| Penerima tidak cocok | {"payout_id": "…", "counterparty_name": "ORANG LAIN"} |
payout.sent dengan recipient_check: "mismatch" |
| Payout dobel | Simulasikan dua kali untuk payout yang sama | payout.duplicate |
Pengembalian dana bank (payout.reversed) belum bisa disimulasikan lewat API.
Riwayat versi
| Versi | Perubahan |
|---|---|
2026-09-28 |
Versi pertama API merchant dan webhook |