Dokumentasi API — Geura

Referensi lengkap buat integrasi ke sistem lain (Laravel, Odoo, n8n, atau bahasa/framework apapun yang bisa HTTP request). Semua contoh di bawah pakai curl, tinggal disesuaikan ke library HTTP client bahasa kamu.

Autentikasi

Semua endpoint di bawah /api/* butuh autentikasi. Ada dua cara, pilih salah satu:

1. API Key — buat integrasi server-ke-server

Kirim sebagai header x-api-key, atau query string ?api_key=... (khusus endpoint yang didesain dibuka langsung di browser, misal gambar QR).

curl https://geura.wibicon.com/api/numbers \
  -H "x-api-key: API_TOKEN_KAMU"

API token kamu bisa dilihat di dashboard: tab Saldo & Kuota (klien) atau tab Klien (admin, buat lihat token tiap klien).

2. JWT Bearer Token — dipakai dashboard web

curl -X POST https://geura.wibicon.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"USERNAME","password":"PASSWORD"}'

Respons berisi token — pakai sebagai header Authorization: Bearer <token> di request selanjutnya. Token berlaku 7 hari.

Semua data (device, pesan, kontak, dst) otomatis ter-scope ke tenant yang lagi login/pakai token — satu tenant gak bisa lihat/akses data tenant lain.

Quick Start: Kirim Pesan

Alur paling umum buat integrasi: pastikan minimal satu Device sudah connected (scan QR sekali lewat dashboard, tab Device), lalu kirim pesan pakai number_id device itu.

POST/api/messages/send
curl -X POST https://geura.wibicon.com/api/messages/send \
  -H "x-api-key: API_TOKEN_KAMU" \
  -H "Content-Type: application/json" \
  -d '{
    "number_id": "ID_DEVICE",
    "to": "6281234567890",
    "message": "Halo dari Geura!"
  }'

Respons 202: {"id": "...", "status": "queued"} — pesan masuk antrian (BullMQ), dikirim dengan jeda otomatis anti-banned, bukan langsung sinkron. Cek status akhirnya lewat GET /api/messages/:id.

Mau kirim WhatsApp Poll native? Ganti field message jadi poll: "poll": {"name": "Pilih menu", "options": ["Nasi Goreng", "Mie Goreng"], "multiple": false}

Device

Satu Device = satu nomor WhatsApp = satu session Baileys (protokol WhatsApp Web, unofficial). Butuh scan QR sekali di awal.

MethodEndpointKeterangan
GET/api/numbersList semua device tenant kamu + status connected
POST/api/numbersTambah device baru ({"label": "Nomor CS"}), mulai proses pairing
GET/api/numbers/:idDetail satu device
GET/api/numbers/:id/qrQR sebagai JSON (data URL base64 PNG)
GET/api/numbers/:id/qr.pngQR sebagai gambar langsung, bisa dibuka browser pakai ?api_key=

Riwayat & Inbox

MethodEndpointKeterangan
GET/api/messages?limit=&offset=Riwayat pesan keluar (terbaru dulu)
GET/api/messages/:idDetail + status satu pesan (queued/sent/failed)
GET/api/inboxPesan WA masuk

Webhook (pesan masuk)

Daripada polling /api/inbox terus-terusan, atur webhook_url kamu sendiri di dashboard (menu Webhook) — tiap ada pesan WA masuk, gateway POST ke URL itu:

{
  "event": "message.received",
  "numberId": "...",
  "from": "6281234567890@s.whatsapp.net",
  "body": "isi pesannya"
}
MethodEndpointKeterangan
GET/api/settings/webhookLihat webhook_url yang diatur
PATCH/api/settings/webhook{"webhook_url": "https://..."}
GET/api/settings/webhook/logRiwayat 100 percobaan kirim webhook terakhir (sukses/gagal)
Kegagalan kirim webhook (URL down, timeout, dll) tidak di-retry otomatis — cuma dicatat di log. Pastikan endpoint kamu selalu up, atau pakai polling /api/inbox sebagai cadangan.

Phonebook

Terbagi 3 submenu: Contact (buku kontak), Group (pengelompokan kontak), dan WA Group (grup WhatsApp asli yang device-nya udah jadi member).

Contact

Bulk add pakai format nomor|var1|var2|... per baris (satu baris = satu kontak) — var1 otomatis jadi Nama, sisanya disimpan sebagai variables[] buat personalisasi ke depannya.

MethodEndpointKeterangan
GET/api/contacts?group_id=List kontak, opsional filter grup
POST/api/contacts{"name","phone","group_id","variables":[]} — tambah satu kontak
POST/api/contacts/bulk{"lines":["081...|Andi|Jakarta"],"group_id","country_code":"62"} — dipakai Add Number & Import
PATCH/api/contacts/:idUpdate kontak
Nomor yang diawali 0 otomatis dinormalisasi pakai country_code (mis. 081... + 62 jadi 6281...). Nomor yang udah lengkap (diawali + atau kode negaranya sendiri) dibiarkan apa adanya.

Group

MethodEndpointKeterangan
GET/api/groupsList grup + jumlah kontak tiap grup
POST/api/groups{"name"}
PATCH/api/groups/:idRename grup (otomatis sinkron ke semua kontak yang kepaut)

WA Group

Beda dari Group di atas (yang cuma pengelompokan kontak internal) — ini grup WhatsApp asli, dibaca live dari Baileys, jadi Device-nya wajib connected dulu.

MethodEndpointKeterangan
GET/api/numbers/:id/groupsList grup WhatsApp yang device ini udah jadi member
POST/api/numbers/:id/groups/send{"group_id","message"}group_id di sini adalah JID grup (diakhiri @g.us)

Template

Pesan tersimpan, dipilih dari dropdown pas Kirim Pesan/Campaign/Recurring biar gak ngetik ulang.

MethodEndpointKeterangan
GET/api/templatesList template
POST/api/templates{"name","content"}
PATCH/api/templates/:idUpdate name/content

Campaign (blast)

Kirim satu pesan ke banyak penerima sekaligus, lewat satu atau beberapa Device (kalau lebih dari satu, beban blast-nya kebagi).

POST/api/campaigns
{
  "name": "Promo Akhir Bulan",
  "number_ids": ["ID_DEVICE_1", "ID_DEVICE_2"],
  "recipients": ["6281111111111", "6282222222222"],
  "message": "Halo, ada promo nih!",
  "delay_min_ms": 2000,
  "delay_max_ms": 5000
}

Cuma nomor dari number_ids yang beneran connected yang dipakai kirim. delay_min_ms/delay_max_ms opsional (default dari setting server) — jeda acak antar pesan buat anti-banned.

Recurring

Kirim ulang isi sebuah Template ke daftar penerima tetap, otomatis sesuai jadwal — dieksekusi scheduler tiap menit.

POST/api/recurring
{
  "name": "Reminder Mingguan",
  "number_id": "ID_DEVICE",
  "template_id": "ID_TEMPLATE",
  "recipients": ["6281111111111"],
  "interval_type": "weekly",
  "start_at": "2026-09-15T09:00:00.000Z"
}

interval_type: daily, weekly, monthly, atau annually.

Autoreply

Balasan otomatis berdasar keyword pesan WA masuk.

POST/api/autoreply
{
  "keyword": "harga",
  "match_type": "contains",
  "reply_text": "Info harga bisa dicek di katalog kami ya!",
  "is_active": true
}

match_type: contains, exact, begin, atau end.

Submission

Form tanya-jawab bertahap lewat chat WA — orang chat trigger_keyword, bot kirim opening_message (opsional) lalu pertanyaan satu-satu, jawabannya tersimpan otomatis.

POST/api/submissions/flows
{
  "name": "Form Pendaftaran",
  "trigger_keyword": "daftar",
  "opening_message": "Yuk isi form pendaftaran singkat ini!",
  "questions": ["Siapa nama kamu?", "Nomor WA aktif?"],
  "thank_you_message": "Terima kasih, pendaftaran kamu udah kami terima!"
}
GET/api/submissions/responses

Lihat semua hasil isian yang udah masuk.

Chaining

Chatbot bercabang (mirip menu IVR) — root node dipicu keyword, balasan orang yang lagi "di" node itu dicocokkan ke key anak-anaknya buat lanjut ke node berikutnya.

POST/api/chains
// Root node (parent_id kosong), dipicu keyword lewat "key"
{ "number_id": "ID_DEVICE", "name": "Menu Utama", "key": "menu", "message": "Ketik 1 buat Sales, 2 buat Support" }

// Node anak, parent_id ke node di atas
{ "number_id": "ID_DEVICE", "name": "Sales", "parent_id": "ID_NODE_ROOT", "key": "1", "message": "Tim Sales akan segera menghubungi kamu" }

Rotator

Link publik yang muter-muter redirect ke salah satu nomor WA tenant — buat tombol "Chat Kami" di website/bio-link biar beban CS kebagi rata.

POST/api/rotators
{
  "name": "Tombol Chat Kami",
  "numbers": [{"number_id": "ID_DEVICE_1", "weight": 2}, {"number_id": "ID_DEVICE_2", "weight": 1}],
  "rotation_cycle": "full",
  "default_text": "Halo, saya mau tanya-tanya"
}

Link publiknya: https://geura.wibicon.com/r/ID_ROTATOR (tanpa auth, langsung redirect ke wa.me). rotation_cycle: full (round-robin berbobot) atau partial (random berbobot).

Follow Up

N jam/hari setelah kontak MASUK ke suatu grup Phonebook, kirim pesan otomatis sekali ke kontak itu (nurture/onboarding sequence).

POST/api/followups
{
  "name": "Follow up 1 hari setelah daftar",
  "number_id": "ID_DEVICE",
  "group_name": "leads-baru",
  "after_value": 1,
  "after_unit": "day",
  "message": "Halo, masih tertarik dengan produk kami?"
}

Telegram

Channel tambahan di luar WhatsApp. Satu tenant bisa punya beberapa bot (masing-masing dari token @BotFather).

MethodEndpointKeterangan
POST/api/telegram/bots{"label","bot_token"} — otomatis diverifikasi & daftarin webhook ke Telegram
POST/api/telegram/bots/:id/send{"chat_id","message"} — kirim sinkron (gak lewat antrian)
GET/api/telegram/messagesRiwayat terkirim
GET/api/telegram/incomingPesan masuk

WABA (WhatsApp Business API resmi Meta)

Beda arsitektur total dari Device (Baileys) — gak ada QR scan. Kamu input phone_number_id + access_token dari Meta App dashboard sendiri.

POST/api/waba/numbers
{
  "label": "Nomor CS Utama",
  "phone_number_id": "1234567890",
  "access_token": "EAAxxxxxxxxxxxxxxxxxxxxxxxx",
  "waba_id": "9876543210"
}
POST/api/waba/numbers/:id/send
{ "to": "6281234567890", "message": "Halo dari WABA!" }
Webhook WABA itu global (satu URL buat semua tenant/nomor, sesuai arsitektur Meta): daftarin https://geura.wibicon.com/waba/webhook di Meta App dashboard, dengan Verify Token sesuai WABA_VERIFY_TOKEN di server. Lihat URL-nya juga dari dashboard, tab WABA, atau GET /api/waba/webhook-info.
Di luar jendela 24 jam sejak pesan terakhir dari pelanggan, Meta cuma izinin kirim template pesan yang udah disetujui — pesan bebas kayak biasa akan ditolak Meta.

Email

Satu setting SMTP (kirim) + IMAP (terima, opsional) per tenant — beda dari Telegram/WABA yang bisa multi-akun.

MethodEndpointKeterangan
PATCH/api/email/settingsSimpan setting SMTP/IMAP (lihat form di dashboard buat field lengkapnya)
POST/api/email/send{"to","subject","message"}
GET/api/email/messagesRiwayat terkirim
GET/api/email/incomingEmail masuk (di-poll dari IMAP tiap 1 menit, kalau diaktifkan)

AI Auto-Reply

Fallback TERAKHIR buat pesan WA/Telegram/WABA masuk — cuma dipanggil kalau gak ada Submission/Chaining/Autoreply yang cocok. Pakai API key AI kamu sendiri (OpenAI atau Anthropic).

PATCH/api/ai
{
  "is_active": true,
  "provider": "openai",
  "api_key": "sk-...",
  "model": "gpt-4o-mini",
  "system_prompt": "Kamu adalah customer service toko online yang ramah dan singkat."
}

GET /api/ai gak pernah balikin api_key utuh, cuma flag has_api_key.

CS Multi Agent

Sistem CS manusia (bukan bot) yang di-assign nanganin percakapan dari semua channel (WA/Telegram/WABA).

MethodEndpointKeterangan
POST/api/divisions{"name"} — grup tim CS
POST/api/agents{"name","email","phone","division_id"}
GET/api/conversations?status=openList percakapan (ter-update otomatis tiap ada pesan masuk)
PATCH/api/conversations/:id{"assigned_agent_id","division_id","status"}
GET/api/conversations/reportRingkasan open/closed per Agent + jumlah yang belum di-assign

Invoices & Billing

Kuota gratis bulanan + top up saldo (deposit) via QRIS Midtrans.

MethodEndpointKeterangan
GET/api/billingKuota terpakai + saldo deposit tenant kamu
GET/api/billing/paymentsRiwayat top up (ditampilin sebagai tab Invoices)
Kuota lewat batas gratis bukan hard block — pesan tetap terkirim, cuma motong saldo deposit (bisa minus). Saldo yang gak kepake hangus tiap ganti bulan.

Setting

MethodEndpointKeterangan
GET/api/settings/profileProfil tenant (nama, perusahaan, telepon)
PATCH/api/settings/profile{"name","company_name","phone"}
PATCH/api/auth/password{"current_password","new_password"} — ganti password sendiri

Rate Limit & Anti-Banned

Channel WA di gateway ini pakai protokol WhatsApp Web (Baileys, unofficial) — bukan API resmi Meta. Buat ngurangin risiko nomor kena banned, tiap pesan blast dikirim lewat antrian dengan jeda acak (default 2–5 detik) antar pesan, bukan sekaligus.

  • Kirim volume besar? Pakai beberapa Device sekaligus (fitur Rotator/Campaign multi-nomor bisa bantu bagi beban).
  • Warm-up nomor baru sebelum dipakai blast volume besar (kirim wajar dulu beberapa hari).
  • Channel Telegram, WABA (resmi Meta), dan Email tidak pakai jeda ini karena bukan protokol unofficial.