Pengembang
Referensi API
Satu endpoint untuk mengirim, satu untuk membaca status, webhook bertanda tangan HMAC untuk tanda terima. Kunci diterbitkan begitu akun dibuat — tidak ada yang perlu disetujui dan tidak ada yang perlu dihubungi lewat email.
Memulai
Tiga hal berdiri di antara akun baru dan pesan yang terkirim: sebuah token, sejumlah saldo, dan sebuah kunci. Tidak satu pun melibatkan manusia.
- Buat akun — browser Anda membuat token, dan token itulah akunnya.
- Isi saldo. Minimum $40, dengan salah satu koin di halaman pembayaran.
- Buat kunci API di dasbor. Kunci itu ditampilkan sekali; kami hanya menyimpan hash-nya, sama seperti token.
Kunci dengan saldo kosong tidak dapat mengirim
Pembuatan kunci sengaja dibatasi hingga akun terisi saldo. Kredensial yang tidak terpakai hanyalah risiko tanpa manfaat, jadi dasbor tidak akan menerbitkan kunci sampai ada saldo untuk dibelanjakan.
Autentikasi
Bearer token pada setiap permintaan. Kunci ini mengidentifikasi akun; tidak ada faktor kedua, tidak ada tanda tangan pada permintaan, dan tidak ada IP allowlist — kunci ini adalah seluruh kredensial, jadi perlakukan sebagai satu kesatuan.
Authorization: Bearer ws_live_9f2c…
| Transport | Hanya HTTPS. Permintaan melalui HTTP biasa ditolak, bukan dialihkan — pengalihan sudah akan membocorkan kunci tersebut. |
|---|---|
| Cakupan | Satu kunci menghabiskan saldo satu akun. Buat satu kunci per sistem yang mengirim, sehingga kebocoran dapat dicabut secara terbatas. |
| Pencabutan | Segera. Kunci yang dicabut mengembalikan 401 pada permintaan berikutnya; batch yang sedang berjalan dan sudah diterima tetap selesai. |
| Pemulihan | Tidak ada. Kami hanya menyimpan hash-nya. Kunci yang hilang diganti, tidak pernah dipulihkan. |
Kirim pesan
POST/v1/messages
Satu panggilan, satu atau banyak penerima, tujuan campuran. Setiap penerima diberi harga sesuai tarif tujuannya sendiri dan ditagih per segmen. Panggilan diterima atau ditolak secara keseluruhan; tidak pernah menagih Anda sebagian.
Isi permintaan
| Bidang | Jenis | Catatan |
|---|---|---|
to | string[] | Wajib. E.164, hingga 5.000 per panggilan. Nomor yang tidak sesuai dengan tujuan yang memiliki tarif akan ditolak sebelum penagihan. |
text | string | Wajib diisi. UTF-8. Jumlah segmen mengikuti GSM 03.38 — lihat segmen. |
from | string | Sender ID alfanumerik, hingga 11 karakter. Dibawa jika rutenya memungkinkan; lihat sender ID. |
vars | object[] | Nilai gabungan opsional, satu objek per penerima, disisipkan ke placeholder {{name}} sebelum segmen dihitung. |
callback_url | string | Penggantian webhook akun opsional per batch. |
reference | string | Opsional. Dikembalikan pada setiap tanda terima agar dapat dicocokkan dengan catatan Anda sendiri. |
Contoh
# one call, many recipients, mixed destinations
curl -X POST https://api.worldsms.io/v1/messages \
-H "Authorization: Bearer $WORLDSMS_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "ACME",
"to": ["+447700900123", "+33612345678"],
"text": "Your code is 480913"
}'
{
"batch_id": "b_7Kq2xR9wLm",
"accepted": 2,
"rejected": 0,
"segments": 1,
"cost_usd": 0.0353
}
$0,0137 ke Inggris ditambah $0,0216 ke Prancis — daftar tarif diterapkan per tujuan, bukan rata-rata gabungan.
Pengelompokan
Sebuah batch diterima segera dan mengalir dengan laju 200 pesan per menit per akun, sehingga 50.000 penerima memerlukan sekitar 4 j 10 mnt. Trafik yang mendesak waktu tidak boleh mengantre di belakang pengiriman pemasaran: gunakan kunci terpisah untuk itu.
Status pesan
GET/v1/messages/{batch_id}
Polling didukung dan dibatasi laju; webhook adalah jalur yang dimaksudkan. Respons merangkum batch dan mencantumkan status per penerima.
{
"batch_id": "b_7Kq2xR9wLm",
"status": "done",
"sending": 0,
"delivered": 1,
"undelivered": 1,
"refund_usd": 0.0216
}
refund_usd diselesaikan sekali, saat batch selesai. Sudah kembali ke saldo Anda pada saat Anda dapat membacanya.
Saldo
GET/v1/balance
Periksa sebelum melakukan pengiriman besar. Saldo disimpan secara internal dalam bilangan bulat micro-dollar — tarif memiliki empat angka desimal, dan angka floating-point tidak punya tempat dalam catatan transaksi — dan ditampilkan di sini dibulatkan ke enam angka desimal.
{ "balance_usd": 412.905400, "currency": "USD" }
Tanda terima pengiriman
Satu tanda terima diterbitkan per penerima sesuai laporan operator. Ini adalah satu-satunya empat status; tidak ada kategori “tidak diketahui” yang diam-diam berarti “kami kehilangan jejaknya”.
| Status | Arti | Ditagih |
|---|---|---|
queued | Diterima dan menunggu giliran dalam batch. | Ya |
sent | Diserahkan ke operator, belum ada konfirmasi. | Ya |
delivered | Ponsel telah mengonfirmasinya. | Ya |
undelivered | Mati, diblokir, atau sender ditolak. | Dikembalikan |
Nomor yang tidak pernah cocok dengan tujuan yang kami beri harga akan ditolak saat pengiriman dan tidak pernah masuk ke siklus ini sama sekali — nomor tersebut tidak ditagih lalu dikembalikan, melainkan memang tidak ditagih sejak awal.
Webhook
Atur endpoint di dasbor atau per batch dengan callback_url. Kami mengirim POST JSON dan mengharapkan 2xx dalam 5 detik. Respons non-2xx dicoba ulang enam kali dengan exponential backoff selama sekitar satu jam, lalu dibatalkan.
Muatan
{
"event": "message.delivered",
"batch_id": "b_7Kq2xR9wLm",
"to": "+447700900123",
"destination": "gb",
"segments": 1,
"cost_usd": 0.0137,
"reference": "order-4471",
"ts": 1785312041
}
Memverifikasi tanda tangan
Setiap pengiriman membawa X-WorldSMS-Signature dan X-WorldSMS-Timestamp. Tanda tangannya adalah HMAC-SHA256(timestamp + "." + body) menggunakan secret webhook Anda. Bandingkan dalam waktu konstan, dan tolak stempel waktu yang lebih tua dari lima menit agar pengiriman yang telah disadap tidak dapat diputar ulang kepada Anda nanti.
// $secret is the webhook secret from the dashboard
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_WORLDSMS_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_WORLDSMS_SIGNATURE'] ?? '';
// A stale timestamp is a replay, however well it is signed.
if (abs(time() - (int) $ts) > 300) { http_response_code(408); exit; }
$mine = hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (!hash_equals($mine, $sig)) { http_response_code(401); exit; }
http_response_code(204);
Bersifat idempoten
Percobaan ulang setelah endpoint Anda timeout terlihat identik dengan pengiriman pertama. Kunci pada batch_id + to + event dan perlakukan pengulangan sebagai no-op.
Kesalahan
Setiap kegagalan mengembalikan isi JSON dengan slug error yang stabil. Cocokkan pada slug, jangan pernah pada teksnya — teksnya boleh berubah menjadi lebih baik.
| HTTP | Slug | Apa yang harus dilakukan |
|---|---|---|
| 400 | invalid_body | JSON tidak valid atau bidang wajib yang hilang. |
| 401 | key_rejected | Kunci tidak ada, dicabut, atau salah ketik. Tidak dapat dicoba ulang. |
| 402 | insufficient_credit | Batch ini berbiaya lebih besar dari saldo. Tidak ada yang terkirim; isi saldo dan kirim ulang. |
| 422 | no_valid_recipients | Setiap nomor gagal dipetakan ke tujuan yang memiliki tarif. |
| 422 | sender_id_invalid | from lebih panjang dari 11 karakter atau berisi string hanya angka yang akan dibaca sebagai angka. |
| 429 | rate_limited | Kurangi laju permintaan. Retry-After membawa jumlah detiknya. |
| 503 | route_unavailable | Tujuan untuk sementara tidak dapat dirutekan. Dapat dicoba ulang. |
{
"error": "insufficient_credit",
"message": "Batch costs $84.20, balance is $12.05.",
"required_usd": 84.20,
"balance_usd": 12.05
}
Batas
| Permintaan | 60 per menit per kunci, lonjakan 20. 429 dengan Retry-After setelah itu. |
|---|---|
| Penerima per panggilan | 5.000. Bagi daftar yang lebih besar ke beberapa panggilan, atau unggah berkas di dasbor. |
| Ukuran isi pesan | 2 MB. |
| Laju pengiriman | 200 pesan per menit per akun, berapa pun jumlah kunci yang mengirimkan. |
| Kunci | Tidak ada batas. Satu per sistem pengirim adalah pola yang dimaksudkan. |
Batas laju berlaku untuk panggilan API, bukan untuk pesan: satu panggilan yang membawa 5.000 penerima dihitung sebagai satu permintaan terhadap batas.
Kunci diterbitkan secara instan. Tidak ada tahap persetujuan.
Isi saldo akun, dan dasbor langsung membuatkannya saat itu juga.