Katalog memerlukan sesi walaupun hanya dibaca. Jangan mengirim nama layanan, kode ISO negara, atau ID provider sebagai pengganti id dari katalog. Quote bukan reservasi nomor dan dapat berubah saat konfirmasi.
Kontrak nyata, nilai contoh
Endpoint, field, dan status HTTP mengikuti implementasi. Nilai ID, nomor, OTP, waktu, saldo, serta quote di contoh adalah ilustrasi, bukan data akun atau pilihan yang bisa langsung dibeli. Ganti dengan data dari instalasimu. Halaman ini tidak mengirim request atau menjalankan transaksi.
JavaScript dijalankan pada origin aplikasi setelah login. Contoh cURL memakai PING_ORIGIN dan cookie jar PING_COOKIE_JAR dari panduan autentikasi. Sukses memakai { ok: true, data }; create juga HTTP 200, bukan 201. Semua endpoint di halaman ini memerlukan sesi aktif dan dapat mengembalikan 401, 403, atau 500.
Mengembalikan layanan yang diaktifkan operator dan masih tercantum di katalog provider. Respons berupa array tanpa pagination. v1 menyertakan reliable dari riwayat penerimaan SMS nyata seluruh pengguna di ping, cukup minimal satu event CODE_RECEIVED tanpa batas usia atau syarat pesanan selesai; hanya boolean agregat yang dikirim tanpa identitas pelanggan atau isi SMS. UI memeriksa setiap 15 detik; katalog master tetap mengikuti sinkronisasi admin, bukan scan stok seluruh produk. Filter kategori/pencarian pada UI dilakukan client-side, bukan lewat parameter endpoint ini.
Daftar layanan
Parameter
Tipe
Wajib
Keterangan
provider (query)
all | v1 | v2 | v3 | v4
Tidak
Tanpa parameter, katalog menggabungkan server aktif. Pilih v1–v4 untuk satu server dan gunakan identitas dari quote untuk pembelian. Nama penyedia internal bukan bagian dari kontrak publik.
GET /api/catalog/services · JavaScript
const response = await fetch("/api/catalog/services?provider=all", {
credentials: "same-origin",
cache: "no-store",
});
const body = await response.json();
if (!response.ok || !body.ok) {
throw Object.assign(
new Error(body.error?.message ?? "Request gagal"),
{ code: body.error?.code, status: response.status },
);
}
const data = body.data;
Daftar layanan
Kode error
HTTP
Tindakan
PROVIDER_ERROR
502
Baca ulang status. Jangan retry pembelian upstream secara otomatis.
PROVIDER_UNAVAILABLE
503
Periksa konfigurasi server; lanjutkan pembacaan dengan backoff.
Daftar negara
GET/api/catalog/countriesSession cookie
Tanpa serviceId mengembalikan katalog negara aktif. Dengan serviceId hanya negara yang memiliki penawaran tersedia pada layanan/server tersebut: Pada katalog gabungan, price_idr mengambil penawaran tersedia termurah dari semua server yang eligible. Opsi yang direkomendasikan memakai harga opsinya sendiri pada konfirmasi pembelian. qualitative_stock merangkum ketersediaan, dan stock_count menjumlahkan stok semua pilihan harga yang tersedia setelah deduplikasi bucket yang sama. Total bukan jaminan atau reservasi stok; null jika ada jumlah yang tidak diketahui atau total tidak aman direpresentasikan. Pembacaan provider digabung per layanan; v1 memakai cache maksimum 10 detik dan menunggu upstream saat kedaluwarsa, server lain tetap 60 detik. UI v1 memeriksa setiap 15 detik saat tab terlihat/online serta saat kembali fokus. Harga mulai adalah pilihan tersedia termurah, tidak mengikuti badge sukses. reliable bernilai true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Hanya boolean yang dikirim, bukan identitas atau isi SMS; bukan jaminan OTP. Kegagalan provider bukan daftar kosong. Gunakan id untuk quote/order. Harga final tetap diperiksa lagi.
Daftar negara
Parameter
Tipe
Wajib
Keterangan
provider (query)
all | v1 | v2 | v3 | v4
Tidak
Tanpa parameter, katalog menggabungkan server aktif. Pilih v1–v4 untuk satu server dan gunakan identitas dari quote untuk pembelian. Nama penyedia internal bukan bagian dari kontrak publik.
serviceId (query)
string
Tidak
GET /api/catalog/countries · JavaScript
const response = await fetch("/api/catalog/countries?provider=all&serviceId=service-example", {
credentials: "same-origin",
cache: "no-store",
});
const body = await response.json();
if (!response.ok || !body.ok) {
throw Object.assign(
new Error(body.error?.message ?? "Request gagal"),
{ code: body.error?.code, status: response.status },
);
}
const data = body.data;
Daftar negara
Kode error
HTTP
Tindakan
VALIDATION_ERROR
400
Periksa tipe, field wajib, format UUID, dan field tambahan.
SERVICE_UNAVAILABLE
422
Muat ulang katalog dan pilih layanan yang tersedia.
PROVIDER_ERROR
502
Baca ulang status. Jangan retry pembelian upstream secara otomatis.
RATE_LIMITED
429
Hormati Retry-After sebelum meminta ulang. Jangan retry pembelian otomatis atau membuat clientRequestId baru saat hasil awal belum pasti.
Kecepatan SMS server
GET/api/catalog/serversSession cookie
Status orderingEnabled menggabungkan pengaturan admin dan konfigurasi provider untuk pesanan baru per server. orderingPauseReason membedakan jeda manual dari provider yang belum dikonfigurasi. Saldo provider tidak menjeda pembelian secara otomatis. Bukan jaminan stok. Statistik milik akun bersesi dari SMS pertama yang tercatat dalam 30 hari terakhir; tidak membaca isi SMS atau histori akun lain. Mengukur purchaseFinishedAt ke event CODE_RECEIVED unik, termasuk jeda polling. Tanpa waktu aktivasi tidak dihitung. Label tercepat memerlukan minimal 3 sampel setiap server aktif; server dijeda dan hasil seri tidak diberi label. Bukan jaminan pengiriman berikutnya. Parameter query ditolak.
GET /api/catalog/servers · JavaScript
const response = await fetch("/api/catalog/servers", {
credentials: "same-origin",
cache: "no-store",
});
const body = await response.json();
if (!response.ok || !body.ok) {
throw Object.assign(
new Error(body.error?.message ?? "Request gagal"),
{ code: body.error?.code, status: response.status },
);
}
const data = body.data;
Kecepatan SMS server
Kode error
HTTP
Tindakan
VALIDATION_ERROR
400
Periksa tipe, field wajib, format UUID, dan field tambahan.
Ambil penawaran harga
GET/api/catalog/quotesSession cookie
Membaca quote satu pasangan layanan/negara pada provider yang dipilih, dengan refresh cache bila diperlukan. Quote v1 kedaluwarsa maksimal 10 detik sejak fetch, termasuk cache lama; server lain tetap 5 menit. UI konfirmasi v1 memeriksa setiap 15 detik dan saat kembali fokus, tanpa polling saat hasil pembelian belum pasti. reliable pada opsi v1 berasal dari riwayat SMS nyata di ping untuk bucket yang sama, bukan jaminan sukses. serviceId dan countryId wajib; parameter query lain di luar kontrak ditolak. Pada katalog gabungan, opsi dengan riwayat OTP 7 hari yang cukup dapat diberi recommended=true dan menjadi pilihan awal konfirmasi; bila beberapa server sama kuatnya, pilihan awal bergiliran antarakun dan berganti tiap jam. Tanpa riwayat cukup, tidak ada opsi yang diberi recommended dan opsi termurah menjadi pilihan awal. Opsi tetap diurutkan dari harga termurah; pengguna dapat memilih harga lain. Rekomendasi bukan klaim kecepatan atau jaminan OTP. Harga dan opsi tetap divalidasi lagi saat pembelian.
Ambil penawaran harga
Parameter
Tipe
Wajib
Keterangan
provider (query)
all | v1 | v2 | v3 | v4
Tidak
Tanpa parameter, katalog menggabungkan server aktif. Pilih v1–v4 untuk satu server dan gunakan identitas dari quote untuk pembelian. Nama penyedia internal bukan bagian dari kontrak publik.
serviceId (query)
string
Ya
Service.id dari katalog.
countryId (query)
string
Ya
Country.id dari katalog.
GET /api/catalog/quotes · JavaScript
const response = await fetch("/api/catalog/quotes?provider=all&serviceId=service-example&countryId=country-example", {
credentials: "same-origin",
cache: "no-store",
});
const body = await response.json();
if (!response.ok || !body.ok) {
throw Object.assign(
new Error(body.error?.message ?? "Request gagal"),
{ code: body.error?.code, status: response.status },
);
}
const data = body.data;
Ambil penawaran harga
Kode error
HTTP
Tindakan
VALIDATION_ERROR
400
Periksa tipe, field wajib, format UUID, dan field tambahan.
SERVICE_UNAVAILABLE
422
Muat ulang katalog dan pilih layanan yang tersedia.
COUNTRY_UNAVAILABLE
422
Muat ulang katalog dan pilih negara yang tersedia.
PRICE_UNAVAILABLE
422
Tawarkan pasangan lain atau coba baca quote lagi nanti.
PROVIDER_ERROR
502
Baca ulang status. Jangan retry pembelian upstream secara otomatis.
RATE_LIMITED
429
Hormati Retry-After sebelum meminta ulang. Jangan retry pembelian otomatis atau membuat clientRequestId baru saat hasil awal belum pasti.
Schema · Service
Schema · Service
Field
Tipe
Wajib
Keterangan
provider
v1 | v2 | v3 | v4
Ya
Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif.
id
string
Ya
ID internal dari katalog server yang dipilih. Bukan nama layanan atau kode provider.
name
string
Ya
Nama layanan untuk ditampilkan.
icon
string
Ya
Kunci ikon layanan, bukan URL gambar.
category
string
Ya
Label kategori untuk tampilan.
description
string
Ya
Sesuai tipe yang ditampilkan.
reliable
boolean
Tidak
Opsional pada katalog v1: true jika layanan pada server ini pernah menerima SMS minimal sekali dari riwayat nyata seluruh pengguna di ping. Tanpa batas usia atau syarat pesanan selesai. Tanpa bukti bernilai false; bukan jaminan OTP atau statistik upstream.
Schema · Country
Schema · Country
Field
Tipe
Wajib
Keterangan
provider
v1 | v2 | v3 | v4
Ya
Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif.
id
string
Ya
ID internal dari katalog server yang dipilih. Bukan kode ISO atau kode provider.
name
string
Ya
Sesuai tipe yang ditampilkan.
flag
string
Ya
Label bendera untuk tampilan; jangan dipakai sebagai identifier.
dial
string
Ya
Kode panggilan negara untuk tampilan.
iso2
string
Ya
Kode negara yang tersedia pada metadata.
price_idr
integer
Tidak
Hanya pada daftar negara dengan serviceId: harga mulai, bukan reservasi.
qualitative_stock
available | limited
Tidak
Hanya pada daftar negara dengan serviceId.
reliable
boolean
Tidak
Hanya pada daftar negara dengan serviceId: true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Tidak mengungkap identitas atau isi SMS; bukan jaminan OTP.
otp_delivery_7d
object
Tidak
Opsional pada daftar negara dengan serviceId: jumlah nomor yang menerima CODE_RECEIVED kanonis dibanding sampel nomor yang dibuat dalam 7 hari, sudah terbit, dan sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED. Pesanan menunggu tanpa OTP dan gagal provisioning dikecualikan. Katalog gabungan hanya menjumlahkan sumber yang sedang visible dan eligible. Persentase baru ditampilkan mulai 10 sampel; tanpa identitas atau isi SMS, bukan peluang atau jaminan OTP berikutnya.
stock_count
integer | null
Tidak
Hanya pada daftar negara dengan serviceId: total stok pilihan harga eligible dan visible menurut filter selector dengan biaya provider maksimal USD0.50 (inklusif); negara yang hanya punya stok di atas batas ini tidak ditampilkan, setelah deduplikasi bucket yang sama. Null jika ada jumlah tidak diketahui atau total tidak aman; laporan pool dapat overlap, bukan jumlah nomor unik atau reservasi.
Schema · Quote
Schema · Quote
Field
Tipe
Wajib
Keterangan
provider
v1 | v2 | v3 | v4
Tidak
Ada pada kuotasi satu provider; tidak dikirim untuk provider=all. Gunakan options[].selection untuk katalog gabungan.
serviceId
string
Ya
Sesuai tipe yang ditampilkan.
countryId
string
Ya
Sesuai tipe yang ditampilkan.
price_idr
integer
Ya
Harga opsi pertama. Konfirmasi memakai harga opsi yang dipilih.
price_min_idr
integer
Tidak
Pada katalog gabungan: harga minimum dari pilihan visible dan eligible.
price_max_idr
integer
Tidak
Pada katalog gabungan: harga maksimum dari pilihan visible dan eligible; bukan batas yang otomatis disetujui.
qualitative_stock
available | limited
Ya
Ketersediaan kualitatif, bukan jumlah stok yang dijamin.
stock_count
integer | null
Ya
Jumlah yang dilaporkan provider untuk pilihan harga ini, bukan jaminan atau reservasi stok. Null jika snapshot cache lama belum menyimpan jumlah.
options
QuoteOption[]
Ya
Pilihan harga yang tersedia saat quote diambil.
fetched_at
string · date-time
Ya
ISO 8601 dengan zona waktu.
expires_at
string · date-time
Ya
Masa berlaku terpendek kandidat quote; bukan reservasi harga atau nomor.
Schema · QuoteOption
Schema · QuoteOption
Field
Tipe
Wajib
Keterangan
id
string
Ya
ID pilihan harga; teruskan sebagai priceOptionId, jangan buat sendiri.
price_idr
integer
Ya
Rupiah utuh, bukan sen atau string berformat.
qualitative_stock
available | limited
Ya
Ketersediaan kualitatif, bukan jumlah stok yang dijamin.
stock_count
integer | null
Ya
Jumlah yang dilaporkan provider untuk pilihan harga ini, bukan jaminan atau reservasi stok. Null jika snapshot cache lama belum menyimpan jumlah.
operator
object
Tidak
Opsional: operator seluler yang dipin pilihan ini ketika didukung provider. Key stabil dan opaque, bukan ID upstream atau nama provider. Kelompokkan menurut key, lalu kirim id opsi sebagai priceOptionId; fallback tetap operator yang sama. Tanpa field ini tidak ada batasan operator.
selection
object
Tidak
Pada katalog gabungan: target pembelian awal yang persis, termasuk provider dan ID sumbernya.
recommended
boolean
Tidak
Opsional pada katalog gabungan: pilihan awal, tetap memakai harga opsi ini. Jika ada riwayat tier valid minimal 10 sampel dan pernah OTP, acuannya batas bawah Wilson 95%; tier lain yang tidak terbukti lebih buruk (batas atas Wilson-nya mencapai acuan itu) dan paling mahal 10% di atasnya bergiliran menjadi pilihan awal per akun, layanan, negara, dan jam. Tanpa bukti cukup, tidak ada opsi yang diberi recommended. Bukan peluang, klaim kecepatan, atau jaminan OTP. Opsi termurah tetap diurutkan lebih dulu dan dapat dipilih.
otp_delivery_7d
object
Tidak
Opsional: received dan samples dari nomor yang dibuat dalam 7 hari pada exact tier upstream pilihan awal. Hanya nomor terbit yang sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED; menunggu tanpa OTP dan gagal provisioning dikecualikan. Menggunakan CODE_RECEIVED kanonis, tidak mencampur provider/produk/operator/tier atau opsi berharga sama. Persentase tampil mulai 10 sampel; bukan peluang atau jaminan OTP berikutnya.
fallback_price_min_idr
integer
Tidak
Pada katalog gabungan: harga minimum pilihan awal dan alternatif eligible. Jika opsi memiliki operator, hanya alternatif dengan operator.key yang sama.
fallback_price_max_idr
integer
Tidak
Pada katalog gabungan: harga maksimum pilihan awal dan alternatif eligible untuk operator yang sama jika dipin, bukan persetujuan otomatis. Harga di atas pilihan awal memerlukan maxPriceIdr eksplisit.
otp_eta_seconds
integer
Tidak
Opsional: median detik ACTIVE ke CODE_RECEIVED pertama pada exact tier upstream. Mengutamakan 6 jam terakhir, lalu histori hingga 7 hari, minimal 1 sampel dengan identity dan latency terverifikasi. Tidak menunggu aktivasi kedaluwarsa atau mensyaratkan persentase keberhasilan; hanya mengukur waktu pada OTP yang masuk, bukan peluang OTP. Pada katalog gabungan mengikuti tier pilihan awal, tidak mencampur histori opsi berharga sama atau provider fallback. Sampel bisa terbatas; bukan jaminan OTP.
last_otp_received_at
string · date-time
Tidak
Opsional: waktu CODE_RECEIVED kanonis terakhir yang valid dalam histori 7 hari pada exact tier upstream pilihan awal. Tidak mencampur provider, produk, operator, atau tier harga; tidak berisi kode OTP atau identitas pengguna. Bukan jaminan OTP berikutnya.
reliable
boolean
Tidak
Opsional untuk v1: true jika server, layanan, negara, produk, operator, dan bucket harga yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna. Termasuk pesanan aktif dan riwayat lama. Tidak memakai stok atau status selesai sebagai bukti, dan bukan jaminan OTP.