3.1 GET /balance
curl -sS "$BASE/balance" -H "Authorization: Bearer $AYOSEL_API_KEY"{ "success": true, "data": { "balance": 250000, "currency": "IDR" } }Akun yang belum pernah punya wallet mendapat balance: 0.
3.2 GET /balance/transactions
Mutasi saldo, terbaru lebih dulu. Memuat semua pergerakan saldo Anda, termasuk top up dan pembelian lewat bot.
| Parameter | Keterangan |
|---|---|
limit | Bawaan 20, maksimum 100 |
offset | Bawaan 0 |
curl -sS "$BASE/balance/transactions?limit=2" -H "Authorization: Bearer $AYOSEL_API_KEY"{
"success": true,
"data": [
{
"id": "44444444-4444-4444-8444-444444444444",
"type": "purchase",
"direction": "debit",
"amount": 36000,
"balance_before": 286000,
"balance_after": 250000,
"reference_type": "order",
"reference_id": "33333333-3333-4333-8333-333333333333",
"description": "Pembelian INV-20261028-33333333",
"created_at": "2026-10-28T03:15:07.482913Z"
},
{
"id": "55555555-5555-4555-8555-555555555555",
"type": "deposit",
"direction": "credit",
"amount": 300000,
"balance_before": 0,
"balance_after": 300000,
"reference_type": "deposit",
"reference_id": "77777777-7777-4777-8777-777777777777",
"description": "Deposit via QRIS - Ref: CONTOH-0001",
"created_at": "2026-10-27T08:00:00.120000Z"
}
],
"meta": { "total": 2, "limit": 2, "offset": 0 }
}| Field | Keterangan |
|---|---|
type | deposit, purchase, refund, bonus_referral, manual_add, atau manual_deduct |
direction | credit bila saldo bertambah, debit bila berkurang |
amount | Nilai mutasi, selalu positif |
balance_before, balance_after | Saldo sebelum dan sesudah mutasi |
reference_type, reference_id | Rujukan mutasi, mis. order dengan id order. reference_id bisa null |
description | Keterangan dari sistem |
created_at | Waktu mutasi |
3.3 POST /orders
Membeli produk dengan saldo wallet. Pemotongan saldo, pembuatan order, pengambilan stok, dan pencatatan mutasi terjadi dalam satu transaksi database. Bila gagal, tidak ada yang terpotong.
Header:
| Header | Keterangan |
|---|---|
Authorization | Bearer <API key> |
Idempotency-Key | Wajib. 8 sampai 64 karakter dari A-Z a-z 0-9 . _ : -. Lihat bagian 3.4 |
Content-Type | application/json |
Body (JSON, maksimal 8 KB):
| Field | Wajib | Keterangan |
|---|---|---|
variant_id | Ya | UUID varian |
quantity | Tidak | Bilangan bulat 1 sampai 20. Bawaan 1 |
Hanya dua field itu yang diterima. Field lain, termasuk kode voucher, ditolak dengan 422 invalid_request. Voucher tidak didukung lewat API.
curl -sS -i -X POST "$BASE/orders" \
-H "Authorization: Bearer $AYOSEL_API_KEY" \
-H "Idempotency-Key: pesanan-20261028-0001" \
-H "Content-Type: application/json" \
-d '{"variant_id":"22222222-2222-4222-8222-222222222222","quantity":2}'Respons sukses, 201 Created:
HTTP/1.1 201 Created
Content-Type: application/json
X-Request-Id: 66666666-6666-4666-8666-666666666666
Cache-Control: no-store
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 19
X-RateLimit-Reset: 60{
"success": true,
"data": {
"id": "33333333-3333-4333-8333-333333333333",
"invoice_number": "INV-20261028-33333333",
"status": "success",
"product_id": "11111111-1111-4111-8111-111111111111",
"product_name": "Akun Streaming Contoh",
"variant_id": "22222222-2222-4222-8222-222222222222",
"variant_name": "1 Bulan",
"quantity": 2,
"unit_price": 18000,
"price_tier": "reseller",
"bulk_discount_amount": 0,
"final_amount": 36000,
"currency": "IDR",
"idempotency_key": "pesanan-20261028-0001",
"created_at": "2026-10-28T03:15:07.482913Z",
"paid_at": "2026-10-28T03:15:07.482913Z",
"refunded_at": null,
"terms": "Garansi 30 hari. Dilarang mengubah kata sandi.",
"items": [
{ "content": "[email protected]|kata-sandi-contoh-1" },
{ "content": "[email protected]|kata-sandi-contoh-2" }
]
}
}Field respons:
| Field | Keterangan |
|---|---|
id, invoice_number | Id order dan nomor invoice |
status | success untuk pembelian yang selesai. Pembelian yang gagal tidak meninggalkan order |
product_id, product_name, variant_id, variant_name | Produk dan varian yang dibeli |
quantity | Jumlah unit |
unit_price | Harga satuan yang ditagih |
price_tier | reseller atau public, tingkat harga yang dipakai |
bulk_discount_amount | Potongan diskon grosir, 0 bila tidak ada |
final_amount | Total yang dipotong dari saldo: unit_price × quantity − bulk_discount_amount |
currency | Selalu IDR |
idempotency_key | Nilai Idempotency-Key yang Anda kirim |
created_at, paid_at | Waktu order dibuat dan dibayar |
refunded_at | Terisi bila toko merefund order, selain itu null |
terms | Syarat dan ketentuan varian. Dibaca saat request, bukan salinan saat order dibuat |
items | Isi produk yang terkirim, satu entri per unit, dalam urutan tetap |
Hal yang perlu diperhatikan:
- Simpan
itemssegera. Isi produk hanya ada di respons API. Tidak dikirim ke bot Telegram, WhatsApp, maupun email. Bila terlewat, ambil lagi lewatGET /orders/{id}/items(bagian 3.8). - Harga yang ditagih adalah harga saat transaksi, sama dengan harga katalog untuk varian dan jumlah yang sama pada saat itu. Periksa
unit_pricedanfinal_amountdi respons. - Total setelah diskon grosir yang kurang dari Rp 1 ditolak dengan
409 variant_unavailable. - Pembelian yang diulang dengan
Idempotency-Keydan body yang sama dijawab200dengan headerIdempotent-Replayed: truedan isi yang sama, tanpa pemotongan baru.
Contoh galat saldo kurang, 402:
{
"success": false,
"error": { "code": "insufficient_balance", "message": "Saldo tidak cukup." },
"request_id": "66666666-6666-4666-8666-666666666666"
}Contoh galat validasi, 422:
{
"success": false,
"error": {
"code": "invalid_request",
"message": "request tidak valid: quantity harus antara 1 dan 20"
},
"request_id": "66666666-6666-4666-8666-666666666666"
}3.4 Idempotency-Key
Jaringan bisa putus setelah server memproses pembelian tetapi sebelum respons sampai. Idempotency-Key membuat pembelian yang diulang tidak memotong saldo dua kali.
Aturan key
- Wajib ada di setiap
POST /orders. Key yang hilang atau salah format dijawab422 invalid_requestdan tidak membuat order. - Format: 8 sampai 64 karakter dari
A-Z a-z 0-9 . _ : -. - Satu key untuk satu niat beli. Buat key baru untuk setiap pembelian baru. UUID acak atau nomor pesanan di sistem Anda cocok.
- Simpan key di sistem Anda sebelum mengirim request pertama, lalu pakai ulang key yang sama untuk setiap percobaan ulang pembelian itu.
- Key berlaku tanpa batas waktu selama ordernya ada. Key milik reseller lain tidak pernah bertabrakan dengan key Anda.
Hasil per kondisi
| Kondisi | Hasil |
|---|---|
| Key baru | 201 dan order baru |
Key sama, variant_id dan quantity sama, order sudah ada | 200 dengan header Idempotent-Replayed: true, order dan items yang sama, tanpa pemotongan baru |
Key sama, tetapi variant_id atau quantity berbeda | 422 idempotency_key_reuse |
| Key sama, request sebelumnya masih diproses | 409 request_in_progress dengan Retry-After: 1 |
| Pembelian sebelumnya gagal tanpa membuat order | Kegagalan tidak disimpan. Request ulang dengan key yang sama diproses dari awal |
quantity yang tidak dikirim dihitung sebagai 1, jadi request pertama tanpa quantity dan percobaan ulang dengan "quantity": 1 dianggap sama.
Mengirim request kembar bersamaan dengan key dan body yang sama menghasilkan paling banyak satu order dan satu pemotongan saldo.
3.5 Mencoba ulang
Kode galat dibagi dua: yang aman diulang dengan key yang sama dan yang tidak boleh diulang begitu saja.
Kode yang aman dicoba ulang dengan key yang sama
| Kode | HTTP | Cara mengulang |
|---|---|---|
insufficient_stock | 409 | Tunggu sebentar lalu ulangi, beberapa kali saja. Kode ini bisa muncul sesaat ketika sisa stok sedang dipegang transaksi lain. Bila tetap muncul, stok memang habis: berhenti dan tampilkan stok habis ke pelanggan Anda |
request_in_progress | 409 | Tunggu sesuai Retry-After (1 detik) lalu ulangi. Request pertama masih berjalan |
server_busy | 503 | Tunggu sesuai Retry-After (1 detik) lalu ulangi |
rate_limited | 429 | Tunggu sesuai Retry-After lalu ulangi |
Request yang menerima salah satu dari keempat kode itu tidak membuat order dan tidak memotong saldo, jadi mengulang dengan key yang sama aman. Hasil ulangannya bisa 201 (baru diproses) atau 200 dengan Idempotent-Replayed: true (sudah diproses oleh request sebelumnya). Retry-After pada rate_limited biasanya hitungan detik, tetapi bisa sampai 300 detik bila yang terlampaui adalah batas kegagalan autentikasi.
Mengulang dengan key yang sama juga aman untuk timeout atau koneksi putus (hasil tidak diketahui), 500 internal_error, dan 503 service_unavailable. Cek header Idempotent-Replayed untuk tahu apakah ini order yang sama.
Kode yang tidak boleh diulang begitu saja
| Kode | Yang dilakukan |
|---|---|
insufficient_balance | Top up saldo dulu. Setelah itu boleh mencoba lagi, karena kegagalan tidak disimpan |
stock_reserved | Stok ditahan pembeli lain. Coba lagi dalam beberapa menit, bukan dalam hitungan detik |
invalid_request, variant_not_found, variant_unavailable | Request atau varian bermasalah. Perbaiki dulu, mengulang yang sama menghasilkan galat yang sama |
idempotency_key_reuse | Key ini sudah dipakai untuk pembelian lain. Bila ini pembelian baru, pakai key baru. Bila ini pembelian yang sama, kirim body yang sama dengan sebelumnya |
invalid_api_key, ip_not_allowed | Perbaiki key atau allowlist lebih dulu. Setiap pengulangan menambah hitungan kegagalan autentikasi (20 per 5 menit per IP) |
account_suspended, api_access_disabled, channel_disabled, plan_not_eligible | Masalahnya ada di akun atau toko. Hubungi toko, mengulang tidak membantu |
Pola mencoba ulang
- Buat key dan simpan.
- Kirim
POST /orders. - Bila berhasil (
201atau200), simpanitemsdan selesai. - Bila kode termasuk daftar bagian 3.5, atau tidak ada respons, tunggu lalu ulangi dengan key yang sama. Pakai
Retry-Afterbila ada, selain itu jeda yang membesar (misalnya 0,5; 1; 2; 4 detik) ditambah sedikit acak. Batasi jumlah percobaan, misalnya 5. - Bila semua percobaan habis dan hasilnya tidak diketahui, cek
GET /orders?idempotency_key=<key>sebelum menyerah. Bila order ada, ambil isinya lewatGET /orders/{id}/items.
Contoh kode ada di bagian 6.1.
3.6 GET /orders
Daftar order yang dibuat lewat API oleh akun Anda, terbaru lebih dulu, tanpa items. Order dari bot atau website tidak ikut.
| Parameter | Keterangan |
|---|---|
status | pending, success, failed, atau expired. Nilai lain dijawab 422 invalid_request |
from, to | Batas waktu, YYYY-MM-DD atau RFC 3339, dalam UTC. Tanggal tanpa jam berarti sehari penuh, jadi to=2026-10-28 mencakup seluruh 28 Oktober. Format lain dijawab 422 invalid_request |
idempotency_key | Nilai Idempotency-Key yang Anda kirim saat membeli. Hasilnya 0 atau 1 order |
limit | Bawaan 20, maksimum 100 |
offset | Bawaan 0 |
curl -sS "$BASE/orders?from=2026-10-01&to=2026-10-28&limit=20" \
-H "Authorization: Bearer $AYOSEL_API_KEY"{
"success": true,
"data": [
{
"id": "33333333-3333-4333-8333-333333333333",
"invoice_number": "INV-20261028-33333333",
"status": "success",
"product_id": "11111111-1111-4111-8111-111111111111",
"product_name": "Akun Streaming Contoh",
"variant_id": "22222222-2222-4222-8222-222222222222",
"variant_name": "1 Bulan",
"quantity": 2,
"unit_price": 18000,
"price_tier": "reseller",
"bulk_discount_amount": 0,
"final_amount": 36000,
"currency": "IDR",
"idempotency_key": "pesanan-20261028-0001",
"created_at": "2026-10-28T03:15:07.482913Z",
"paid_at": "2026-10-28T03:15:07.482913Z",
"refunded_at": null
}
],
"meta": { "total": 1, "limit": 20, "offset": 0 }
}Field setiap order sama dengan field order di POST /orders, tanpa terms dan items.
idempotency_key berguna bila respons POST /orders hilang di jalan:
curl -sS "$BASE/orders?idempotency_key=pesanan-20261028-0001" \
-H "Authorization: Bearer $AYOSEL_API_KEY"meta.total bernilai 1 bila order itu ada dan 0 bila tidak ada.
3.7 GET /orders/{id}
Detail satu order, tanpa items.
curl -sS "$BASE/orders/33333333-3333-4333-8333-333333333333" \
-H "Authorization: Bearer $AYOSEL_API_KEY"data berbentuk sama dengan satu elemen di GET /orders. Order yang tidak ada, milik pelanggan lain, atau dibuat lewat bot atau website dijawab 404 order_not_found, dengan jawaban yang identik untuk ketiga kasus.
3.8 GET /orders/{id}/items
Isi produk yang terkirim untuk satu order, ditambah syarat dan ketentuannya. Dipakai bila Anda perlu mengambil ulang produk, misalnya untuk pelanggan yang kehilangan produknya.
curl -sS "$BASE/orders/33333333-3333-4333-8333-333333333333/items" \
-H "Authorization: Bearer $AYOSEL_API_KEY"{
"success": true,
"data": {
"order_id": "33333333-3333-4333-8333-333333333333",
"terms": "Garansi 30 hari. Dilarang mengubah kata sandi.",
"items": [
{ "content": "[email protected]|kata-sandi-contoh-1" },
{ "content": "[email protected]|kata-sandi-contoh-2" }
]
}
}Isi produk tetap bisa diambil setelah order direfund (refunded_at terisi). Pemilik order dicek seperti di GET /orders/{id}, dan jawaban bila bukan milik Anda adalah 404 order_not_found.
Butuh bantuan dengan integrasi?
Sertakan request_id dari respons galat, lalu hubungi tim Ayosel lewat Telegram atau WhatsApp, atau email [email protected].