REST API — Kirim Email
Satu endpoint untuk mengirim email: POST /v1/messages. Kirim JSON dengan API key akun Anda, dan Hubina menangani validasi sender, saldo, pengiriman, serta pencatatan status.
Base URL
https://api.hubina.net/v1REST API hanya dilayani dari host api.hubina.net. Semua respons berformat JSON dan dikirim denganCache-Control: no-store.
Autentikasi
Setiap permintaan kirim memakai API key di header Authorization:
Authorization: Bearer hb_live_YOUR_API_KEY
Content-Type: application/jsonKey yang tidak valid atau sudah dicabut dibalas 401 tanpa detail tambahan. Cara membuat key ada di Autentikasi & API Key.
POST /v1/messages
Mengirim satu email; satu penerima maupun beberapa sekaligus (maksimum 50 alamat per permintaan).
Parameter request
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
from | string | ya | Alamat pengirim. Domainnya harus milik akun Anda dan berstatus terverifikasi. |
to | string | string[] | ya | Satu alamat atau array alamat. Maksimum 50 alamat. Setiap alamat divalidasi formatnya. |
subject | string | ya | Subjek, maksimum 998 karakter, tidak boleh kosong. |
text | string | salah satu | Isi versi teks biasa. Maksimum 1.000.000 karakter. |
html | string | salah satu | Isi versi HTML. Maksimum 1.000.000 karakter. Boleh dikirim bersama text. |
raw | string | tidak | Pesan MIME lengkap (header + body) yang diteruskan apa adanya ke penyedia. Pakai hanya bila Anda memang menyusun MIME sendiri. |
Minimal salah satu dari text, html, atau raw harus ada. Field internal Hubina (user_id, rate, cost, dan sejenisnya) akan ditolak jika ikut dikirim.
Contoh permintaan
curl -X POST https://api.hubina.net/v1/messages \
-H "Authorization: Bearer hb_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-2211-kwitansi" \
-d '{
"from": "[email protected]",
"to": ["[email protected]", "[email protected]"],
"subject": "Kwitansi pesanan #2211",
"text": "Terima kasih, pesanan Anda sedang diproses.",
"html": "<p>Terima kasih, pesanan <strong>#2211</strong> sedang diproses.</p>"
}'const response = await fetch('https://api.hubina.net/v1/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HUBINA_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `invoice-${invoice.id}-kwitansi`,
},
body: JSON.stringify({
from: '[email protected]',
to: [customer.email],
subject: `Kwitansi pesanan ${invoice.id}`,
text: 'Terima kasih, pesanan Anda sedang diproses.',
}),
});
if (!response.ok) {
const problem = await response.json();
throw new Error(`Hubina ${response.status} ${problem.error}: ${problem.message}`);
}
const message = await response.json();
console.log('diterima untuk dikirim:', message.id, message.status);Respons sukses
Permintaan yang valid dan berhasil diserahkan dibalas 202 Accepted:
{
"id": "msg_1a2b3c4d5e6f77889900aabbccddeeff",
"status": "accepted"
}Jika permintaan yang sama diulang dengan Idempotency-Key yang sama, respons mengembalikan pesan yang sudah ada dan menandai duplicate: true — saldo tidak dipotong dua kali:
{
"id": "msg_1a2b3c4d5e6f77889900aabbccddeeff",
"status": "accepted",
"duplicate": true
}Idempotency dan retry
Header Idempotency-Key mengikat satu permintaan logis ke satu pesan. Formatnya 8–200 karakter, diawali huruf/angka, boleh berisi huruf, angka, titik, garis bawah, titik dua, dan tanda hubung. Pakai nilai stabil dari domain Anda, misalnya order-10231-receipt.
| Situasi | Hasil |
|---|---|
| Key sama, payload sama | 202 dengan duplicate: true; pesan lama dikembalikan, tanpa tagihan baru |
| Key sama, payload berbeda | 409 billing_conflict — key sudah terpakai untuk isi lain |
| Key sama yang sebelumnya gagal dan dananya sudah dikembalikan | 409 billing_conflict — buat key baru untuk percobaan baru |
| Tanpa header | Pesan baru tanpa proteksi duplikat; request ulang bisa terkirim dua kali |
Kapan boleh retry
429dan503: boleh diulang dengan jeda bertahap (backoff), pakaiIdempotency-Keyyang sama.400dan403: perbaiki permintaan dulu — mengulang persis sama akan gagal lagi.- Timeout tanpa balasan: ulangi dengan
Idempotency-Keyyang sama agar aman dari kiriman ganda.
Rate limit
Permintaan kirim dibatasi 60 permintaan per menit per kombinasi akun + API key. Melebihi batas menghasilkan 429 beserta retryAfter (detik):
{
"error": "rate_limited",
"message": "Terlalu banyak permintaan.",
"retryAfter": 17
}Kode error
| Status | error | Artinya | Yang harus dilakukan |
|---|---|---|---|
| 400 | invalid_request | JSON tidak valid, field wajib kosong, penerima tidak valid, subjek/isi melebihi batas, atau body terlalu besar | Perbaiki payload. Pesan message menyebut field-nya. |
| 401 | unauthorized | API key tidak ada, salah, atau dicabut | Cek header Authorization; buat key baru bila perlu. |
| 403 | sender_not_authorized | Domain pengirim bukan milik akun ini atau belum terverifikasi | Verifikasi domain di dashboard, atau pakai alamat dari domain yang sudah terverifikasi. |
| 403 | insufficient_balance | Saldo tersedia kurang dari rate akun | Top up saldo. |
| 409 | billing_conflict | Idempotency-Key sudah dipakai untuk payload lain, atau sebelumnya sudah gagal | Pakai key baru untuk permintaan baru. |
| 429 | rate_limited | Melebihi 60 permintaan per menit | Tunggu sesuai retryAfter. |
| 503 | provider_unavailable | Penyedia pengiriman sedang tidak bisa dihubungi | Retry dengan backoff; tidak ada tagihan untuk percobaan yang gagal total. |
| 500 | server_error | Kesalahan tak terduga di sisi Hubina | Retry dengan Idempotency-Key yang sama; hubungi dukungan bila berulang. |
Batas yang berlaku
- Penerima: maksimum 50 alamat per permintaan.
- Subjek: maksimum 998 karakter. Isi: maksimum 1.000.000 karakter per bagian (
textdanhtmlmasing-masing). - Body JSON: maksimum 2 MB. Untuk pesan lebih besar (sampai 10 MB) gunakan SMTP submission.
- Rate limit: 60 permintaan per menit per akun + key.
Status akhir tidak ada di sini
Respons 202 tidak memuat status akhir pengiriman. Pantau Sending & Logs
di dashboard untuk melihat delivered, bounced, atau rejected.