Dokumentasi · API

API v1

REST dan JSON. Aplikasi Onix sendiri memakai API yang sama, jadi data yang kamu lihat di aplikasi datang lewat endpoint ini.

Base URL: https://onix.sassly.ai/api/v1. Semua path di halaman ini relatif terhadap base URL. Body request berformat JSON (Content-Type: application/json). Spesifikasi lengkap OpenAPI 3.1: /docs/openapi.yaml — bisa diimpor ke Postman, Insomnia, atau generator SDK.

Autentikasi

Aplikasi Onix (browser)Integrasi (server-mu)
IdentitasCookie sesi setelah masuk dengan GoogleHeader Authorization: Bearer onx_live_…
Request yang mengubah dataWajib header X-CSRF-Token milik sesi; aplikasi mengirimnya otomatisTanpa CSRF; key ber-scope write
Hak aksesSesuai role dan akses channel penggunaSeluruh workspace, dibatasi scope key
PaketSemua paketPro dan Custom

Membuat API key

  1. Buka Pengaturan → API (butuh akses Kelola API key; khusus Pro).
  2. Klik Buat key, beri nama (mis. "Integrasi CRM"), lalu pilih scope: read (hanya membaca) atau write (membaca dan mengubah data).
  3. Key berawalan onx_live_ dan hanya ditampilkan sekali. Simpan di secret manager; jangan taruh di kode frontend atau repositori.
  4. Maksimal 10 key aktif per workspace. Request dengan key yang sudah dicabut dibalas 401.
export ONIX_API_KEY="onx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

API key bertindak atas nama workspace dan tidak dibatasi role, jadi perlakukan seperti password. Endpoint pengelolaan (anggota, role, pengaturan, API key, tagihan) hanya bisa dari aplikasi. API tidak mendukung CORS: panggil dari server, bukan dari JavaScript di browser.

Format respons

Respons sukses selalu berbentuk {"data": …, "meta": {…}}; meta hanya ada bila relevan. Respons error selalu berbentuk:

{
  "error": {
    "code": "validation_error",
    "message": "Nama workspace 2–120 karakter.",
    "fields": {"name": "Nama workspace 2–120 karakter."}
  }
}
  • code tetap dan aman dipakai di logika program; message adalah teks untuk manusia yang mengikuti bahasa (header X-Locale: en|id).
  • fields hanya ada pada error input, berisi pesan per kolom.
  • Waktu selalu UTC dalam ISO 8601, mis. 2026-10-08T02:30:00Z. Ubah ke zona waktumu sendiri saat menampilkan; aplikasi Onix memakai zona pribadi pengguna atau zona workspace.
  • Daftar berhalaman menerima page (mulai 1) dan per_page (maks. 100, bawaan 25 kecuali disebut lain), lalu mengembalikan meta.page, meta.per_page, meta.total, dan meta.pages.

Batas request

  • 120 request per menit per API key.
  • Respons memuat header X-RateLimit-Limit dan X-RateLimit-Remaining.
  • Lewat batas dibalas 429 rate_limited dengan header Retry-After (detik sampai menit berikutnya).

Kode error

HTTPcodeArti
401unauthorizedAPI key tidak ada, formatnya salah, atau sudah dicabut; atau sesi aplikasi sudah berakhir.
402plan_requiredWorkspace bukan Pro atau Custom yang aktif (API key dan fitur khusus Pro).
403forbiddenTidak punya permission, endpoint khusus aplikasi dipanggil dengan API key, atau key read mencoba mengubah data.
404not_foundEndpoint atau data tidak ada, termasuk data milik workspace lain.
405method_not_allowedMethod tidak didukung endpoint ini; lihat header Allow.
409conflict / workspace_requiredData bentrok dengan keadaan sekarang (mis. role masih dipakai), atau pengguna belum punya workspace.
419csrf_mismatchRequest aplikasi selain GET tanpa X-CSRF-Token yang valid. Muat ulang halaman lalu coba lagi.
422validation_errorInput tidak valid; rincian per kolom ada di error.fields.
423workspace_frozenWorkspace dibekukan karena kontrak Custom berhenti. Hanya Paket & Tagihan, pindah kepemilikan, notifikasi, dan data baca dasar yang tetap jalan.
429rate_limitedTerlalu banyak request; tunggu sesuai Retry-After.
502ai_errorPenyedia AI sedang gangguan. Kuota AI tidak berkurang; coba lagi sebentar.
503ai_unavailableFitur AI belum diatur di server Onix. Hubungi admin Onix.
500server_errorGangguan di server. Aman dicoba lagi beberapa saat kemudian.

Endpoint

Kolom Permission menunjukkan hak akses yang dibutuhkan pengguna aplikasi (diatur lewat role); "—" berarti semua anggota workspace. API key dianggap punya semua permission workspace dan melihat semua data.

Batas data (tim): permission mengatur menu & aksi, sedangkan data percakapan & tiket yang terlihat mengikuti tim. Percakapan yang belum di-assign terlihat semua yang punya interactions.view; yang di-assign ke tim Frontline hanya terlihat anggota tim itu; yang di-assign ke orang hanya terlihat orang itu. Tiket tanpa penanggung jawab terlihat anggota tim Back Office-nya; tiket dengan penanggung jawab hanya terlihat peserta (penanggung jawab, pembuat, yang di-@mention). Artikel knowledge base khusus tim hanya terlihat anggota tim yang dipilih (tanpa tim = semua tim). Lead pipeline tanpa PJ terlihat anggota tim lead-nya; lead ber-PJ hanya terlihat PJ-nya. Owner, role admin, dan anggota "Bisa lihat semua data" melihat semuanya (akses channel tetap berlaku). Data yang tidak boleh dilihat dibalas 404.

Bisa dipanggil dengan API key

EndpointPermissionKeterangan
GET /me—Pengguna, workspace aktif, role, permission, dan zona waktu efektif. Lewat API key: pengguna = pembuat key, via = "key".
GET /workspace—Workspace aktif: nama, paket, masa aktif, zona waktu, dan batas paket.
GET /dashboarddashboard.viewLangkah awal, pemakaian kuota, aktivitas 14 hari, aktivitas terbaru, dan ringkasan tim (bagian tertentu butuh permission tambahan).
GET /channels—Channel yang boleh diakses (type whatsapp/email/livechat/api, status, pengaturan, icon_url, hours = jam kerja sendiri atau null, auto_reply; livechat juga kunci publik & kode sematan, API juga URL webhook — tanpa kunci & secret), beserta kuota channel (gabungan semua jenis).
GET /channels/{id}/icon—Ikon/logo unggahan channel (pengelola channel atau anggota dengan akses channel itu); 404 bila memakai ikon bawaan.
GET /interactionsinteractions.viewInbox: percakapan per kind (chat/group) dan tab (new = belum dibalas sama sekali, waiting = menunggu balasan, unreplied = keduanya, replied, solved, closed). Setiap item punya reply_state. Filter channel_ids (satu atau beberapa id channel dipisah koma, mis. 1,3; channel_id lama tetap diterima), assignee (me/none/id), team (mine/none/id), label_id, contact_id, q, created_from/created_to (tanggal masuk, YYYY-MM-DD zona pembaca), category (id = kategori itu beserta semua subkategorinya, atau none). meta.counts = jumlah per tab; meta.teams = tim Frontline untuk filter. Di Pro/Custom setiap item punya sla (target balas dalam jam kerja: level warn/breach, menit, tenggat). Hanya percakapan yang boleh dilihat (batas data tim).
GET /interactions/{id}interactions.viewSatu percakapan: kontak/grup, channel, status, tim, assignee, label WhatsApp, waktu respons, custom_fields (field kustom interaction + nilainya), notes_count, tickets (SEMUA tiket dari percakapan ini — juga tanpa akses tiket — masing-masing dengan can_open, update_request, sla, dan thread dengan tim tiket), dan untuk pemegang contacts.view data & field kustom kontak (contact.details, contact.custom_fields).
POST /interactions/{id}/tickets/{ticket}/messagesinteractions.replyDari percakapan ke salah satu tiketnya (tanpa perlu akses tiket): {"text": "…"} mengirim pesan ke diskusi tiket; {"request_update": true} (teks opsional) meminta update — tiket menampilkan update_request sampai tim mengirim feedback/kesimpulan atau menyelesaikannya. Hanya tiket aktif (422), paling sering sekali per 15 menit per tiket (429). Hasil {"message", "tickets"}.
GET /interactions/{id}/notesinteractions.viewCatatan internal percakapan (terbaru dulu, maks. 200), termasuk feedback & kesimpulan tiket (meta.source). Tulis catatan lewat POST /interactions/{id}/messages dengan type = note.
GET /interactions/{id}/messagesinteractions.viewPesan urut lama → baru. Kursor before/after (id pesan), limit maks. 100.
POST /interactions/{id}/messagesinteractions.replyKirim pesan (scope write): {"text": "…"}, atau multipart/form-data dengan file. Jenis lain: note, sticker, location, contact; kutip dengan reply_to_id; template dengan template_id. Percakapan email: html (format minimal: tebal, miring, garis bawah, daftar, tautan; text dibuat otomatis), cc (dipisah koma, maks 10), reply_all, dan beberapa lampiran files[] (multipart, maks 10 file, total 20 MB); penerima, subjek "Re: …", kutipan email sebelumnya, dan tanda tangan diisi otomatis. Di nomor WhatsApp dengan inisial agen aktif, inisial pengirim ditambahkan di baris terakhir (tidak lewat API key). Percakapan livechat & channel API: teks, satu lampiran, atau catatan (tanpa kutip, stiker, lokasi, kontak); membalas livechat yang belum diambil sekaligus mengambilnya (tidak lewat API key).
PATCH /interactions/{id}interactions.replyUbah status (open/solved/closed), team_id (tim Frontline), assignee_id, category_id (kategori percakapan, null = tanpa kategori), dan/atau custom (field kustom interaction {kunci: nilai}; nilai kosong menghapus). Memindah tim atau menugaskan orang lain butuh interactions.assign; orang harus anggota tim percakapan (admin boleh tanpa tim). Hasil {"id", "visible": false} bila pemanggil tidak lagi boleh melihatnya.
PATCH /interactions/{id}/messages/{message}interactions.replyEdit pesan teks sendiri (maks. 15 menit setelah dikirim).
DELETE /interactions/{id}/messages/{message}interactions.replyTarik pesan (hapus untuk semua), maks. 2 hari.
POST /interactions/{id}/messages/{message}/retryinteractions.replyKirim ulang pesan yang gagal.
POST /interactions/{id}/messages/{message}/reactioninteractions.replyReaksi emoji: {"emoji": "👍"}; kosong = hapus.
GET /interactions/{id}/assigneesinteractions.replyAnggota yang bisa ditugaskan ke percakapan ini (dengan team_ids) dan tim Frontline beserta anggotanya di meta.teams.
GET /messages/{id}/mediainteractions.viewFile media pesan (privat). Mendukung Range; ?download=1 untuk mengunduh.
GET /messages/{id}/emailinteractions.viewEmail asli (HTML yang sudah dibersihkan, tanpa skrip) untuk ditampilkan di bingkai aman. Gambar dari internet diblokir; ?images=1 memuatnya.
GET /messages/{id}/attachments/{index}interactions.viewLampiran email ke-index (mulai 0, urutan email.attachments pesan). ?download=1 untuk mengunduh.
GET /contacts/{id}/avatarinteractions.viewFoto profil WhatsApp kontak.
GET /groups/{id}/avatarinteractions.viewFoto grup WhatsApp.
POST /interactionsinteractions.replyMulai chat dengan kontak (scope write): {"contact_id", "channel_id", "text"}. Percakapan terbuka dengan kontak itu di nomor yang sama dipakai lagi. Channel email: wajib subject, isi html atau text, opsional cc; selalu membuat percakapan baru ke email kontak.
POST /interactions/{id}/feedback-seeninteractions.viewTandai feedback/kesimpulan tiket di percakapan sudah dibaca.
GET /groups/{id}interactions.viewHalaman grup: info, anggota (dicocokkan dengan kontak), dan percakapan grup.
GET /ticketstickets.viewDaftar tiket yang boleh dilihat (batas data tim). Filter status (active, open, …), priority, assignee (me/none/id), team (mine/id), q, contact_id, interaction_id, overdue. meta.counts = jumlah per status, meta.teams = tim Back Office untuk filter.
POST /ticketstickets.createBuat tiket (scope write): subject, team_id (tim Back Office, wajib), assignee_ids (opsional, anggota tim itu), description, priority, due_date, interaction_id atau contact_id, dan custom (field kustom tiket {kunci: nilai}). Kuota tiket bulanan berlaku (402).
GET /tickets/candidatestickets.createAnggota yang bisa ditugaskan atau disebut di tiket, dengan team_ids Back Office & is_admin.
GET /tickets/{id}tickets.viewDetail tiket, tim, peserta, izin pemanggil, custom_fields (field kustom tiket + nilainya), dan cuplikan percakapan asal.
PATCH /tickets/{id}tickets.manageUbah status, priority, due_date, subject, description, team_id (pindah tim melepas penanggung jawab yang bukan anggota tim baru), atau custom (field kustom tiket; field yang berubah dicatat di log).
POST /tickets/{id}/assigneestickets.manageTambah penanggung jawab (anggota tim tiket atau admin): {"user_id": 5}.
DELETE /tickets/{id}/participants/{user}tickets.manageKeluarkan peserta (pengikut boleh berhenti mengikuti sendiri). Tanpa penanggung jawab, tiket kembali terlihat seluruh tim.
GET /tickets/{id}/messagestickets.viewDiskusi tiket + log di meta.events; after_message/after_event untuk yang baru saja.
POST /tickets/{id}/messagestickets.viewKirim pesan diskusi: {"body": "…", "mention_ids": [5]}, atau multipart dengan files[] (maks. 5). @Nama di teks juga dikenali; yang disebut ikut melihat tiket.
POST /tickets/{id}/messages/{message}/feedbacktickets.viewKirim pesan diskusi sebagai feedback ke percakapan asal (catatan internal).
POST /tickets/{id}/conclusiontickets.viewKesimpulan untuk agen: {"conclusion": "…", "resolve": true} (resolve butuh tickets.manage).
GET /ticket-messages/{id}/files/{index}tickets.viewLampiran diskusi (privat). ?download=1 untuk mengunduh.
GET /teams—Tim Frontline & Back Office beserta anggotanya; ?type=frontline|backoffice.
GET /teams/{id}—Satu tim.
GET /contactscontacts.viewDaftar kontak. Filter q, label_id, wa_label_id, owner (me/none/id), channel_id, sort (recent/name/created).
POST /contactscontacts.manageTambah kontak: name, phone, email, company, job_title, address, city, language, owner_id, label_ids, custom.
GET /contacts/{id}contacts.viewProfil lengkap: identitas, label Onix & WhatsApp, field kustom, statistik & aktivitas 12 bulan.
PATCH /contacts/{id}contacts.manageUbah kontak (kolom yang dikirim saja). label_ids mengganti seluruh label.
POST /contacts/{id}/labelscontacts.managePasang label Onix: {"label_id": 3} (juga boleh dengan interactions.reply).
DELETE /contacts/{id}/labels/{label}contacts.manageLepas label Onix.
GET /contacts/{id}/timelinecontacts.viewLinimasa kontak, terbaru dulu (before = id terakhir).
GET /contacts/{id}/ticketscontacts.viewTiket kontak beserta log statusnya (hanya yang boleh dilihat).
GET /contacts/{id}/notescontacts.viewCatatan tim.
POST /contacts/{id}/notescontacts.viewTambah catatan: {"body": "…"}.
PATCH /contacts/{id}/notes/{note}contacts.viewUbah catatan (hanya penulisnya).
DELETE /contacts/{id}/notes/{note}contacts.viewHapus catatan (penulisnya atau contacts.manage).
GET /labelscontacts.viewLabel Onix beserta jumlah kontaknya; meta.colors = warna yang tersedia.
GET /custom-fieldscontacts.view / interactions.view / tickets.view / leads.viewDefinisi field kustom per entitas: ?entity=contact|interaction|ticket|lead|client (tanpa entity = semua). meta.max_per_entity = 30.
GET /contact-fieldscontacts.viewDefinisi field kustom kontak (sama dengan /custom-fields?entity=contact).
GET /wa-label-rulescontacts.manageAturan otomasi label WhatsApp (Pro): {"label_name", "channel", "action", "value", "value_label", "active"}. Juga boleh dengan channels.manage.
GET /conversation-categoriesinteractions.viewKategori percakapan dalam urutan pohon (induk lalu subkategorinya): {"id", "name", "color", "position", "parent_id", "depth", "path_text", "children_count"}. Juga boleh dengan contacts.view.
GET /kb/categorieskb.viewKategori knowledge base (urut posisi) dengan jumlah artikel terbit yang boleh dilihat pemanggil.
POST /kb/categorieskb.manageBuat kategori: {"name", "description", "position"}. Nama unik per workspace.
PATCH /kb/categories/{id}kb.manageUbah kategori (kolom yang tidak dikirim tetap).
DELETE /kb/categories/{id}kb.manageHapus kategori; artikelnya menjadi tanpa kategori.
GET /kb/articleskb.viewDaftar artikel berhalaman. Filter q (setiap kata wajib ada), category_id (id atau none), visibility, status, team_id (id tim = artikel khusus tim itu, none = untuk semua tim). Tanpa kb.manage hanya artikel terbit. Artikel khusus tim hanya untuk anggota tim itu (Owner, admin, "Bisa lihat semua data", dan API key melihat semua). Setiap artikel punya teams (kosong = semua tim). meta.counts = jumlah terbit & draf.
POST /kb/articleskb.manageBuat artikel: {"title", "body", "category_id", "visibility", "status", "tags", "team_ids"}. Bawaan Internal + Draf, untuk semua tim. Isi = Markdown sederhana. team_ids = tim yang boleh membaca; pengelola yang tidak melihat semua data hanya boleh memilih timnya sendiri (422).
GET /kb/articles/{id}kb.viewSatu artikel lengkap dengan isi, teams, status pemrosesan pencarian makna (embedding_status, chunk_count, embedded_at_text), dan can.insert (artikel Publik terbit boleh disisipkan ke balasan). Artikel khusus tim lain → 404.
PATCH /kb/articles/{id}kb.manageUbah artikel (kolom yang tidak dikirim tetap; team_ids: [] = semua tim). Terbit & judul/isi berubah → diproses ulang untuk pencarian makna.
DELETE /kb/articles/{id}kb.manageHapus artikel.
GET /kb/searchkb.viewCari artikel terbit. mode=keyword (bawaan, semua paket) atau mode=semantic (Pro/Custom, ai.use, 1 kuota AI per pencarian). Parameter q, visibility, limit (1–20). Hasil: {"article", "match", "score", "snippet"}; meta.ai = pemakaian AI bulan ini.
GET /reports/summaryreports.viewKartu ringkasan untuk rentang from–to (YYYY-MM-DD menurut zona pembaca, inklusif; bawaan 30 hari terakhir, maks 366 hari) dan filter opsional channel_id, team_id: percakapan masuk, pesan masuk/keluar, respons pertama (rata-rata, median, % dalam target), waktu penyelesaian, belum dibalas sekarang, tiket. Rumus: Rumus laporan.
GET /reports/dailyreports.viewSatu baris per tanggal dalam rentang: {"date", "conversations", "messages_in", "messages_out"} (0 bila kosong). Parameter sama dengan ringkasan.
GET /reports/hourlyreports.view24 baris pesan masuk per jam 0–23 (zona pembaca): {"hour", "messages_in"}.
GET /reports/breakdownreports.viewtype = channel, label, category, atau team: baris per kelompok dengan jumlah percakapan masuk (per nomor juga pesan & rata-rata respons pertama; per tim juga tiket dibuat/selesai; per kategori urut pohon dengan conversations termasuk subkategori dan conversations_direct).
GET /kpireports.viewKartu KPI per orang (Pro/Custom; Free → 402): frontline (respons pertama, balasan lanjutan, penyelesaian, Solved per hari kerja, kontak kembali, eskalasi, lewat target sekarang) dan backoffice (respons tiket, penyelesaian tepat waktu per prioritas, selesai per hari kerja, dibuka lagi, feedback, backlog). Waktu dalam menit/jam kerja; from/to (zona workspace, maks 93 hari), team_id, channel_type (whatsapp/email/livechat/api: hanya percakapan & tiket dari jenis channel itu, dengan target jenis itu — email, livechat, dan API punya target sendiri). Lihat KPI & SLA.
GET /kpi/users/{id}—KPI satu orang dengan daily, breaches, dan targets (beserta sumbernya). Diri sendiri selalu; orang lain butuh reports.view.
GET /kpi/me—KPI-mu hari ini & 7 hari terakhir (kartu "KPI saya" di Dashboard).
GET /kpi/configsettings.workspaceTarget workspace, target tim & orang, daftar metrik (termasuk target email email_first_response & email_reply dalam jam, livechat livechat_first_response & livechat_reply dan channel API api_first_response & api_reply dalam menit), dan jam kerja workspace.
GET /reports/agentsreports.viewProduktivitas setiap anggota aktif: {"user", "replies", "conversations", "first_responses", "avg_first_response_minutes", "tickets_resolved"}.
POST /interactions/{id}/summaryai.useRingkasan AI percakapan (Pro/Custom, 1 kuota AI): 3–6 poin, disimpan dan terlihat di GET /interactions/{id} → ai_summary.
GET /templatesinteractions.replyTemplate balasan. Dengan interaction_id, tiap template membawa rendered (variabel terisi). Filter q, category.
GET /templates/{id}interactions.replySatu template.
GET /templates/{id}/attachmentinteractions.replyLampiran template.
GET /settingssettings.workspacePengaturan General (zona waktu workspace, target respons, jam kerja business_hours + open_now, …) dan pilihannya di meta.choices.
GET /memberssettings.membersSemua anggota termasuk Owner.
GET /invitationssettings.membersUndangan yang belum diterima, termasuk yang kedaluwarsa.
GET /rolessettings.rolesSemua role beserta permission dan jumlah pemakai (atau settings.members).
GET /roles/{id}settings.rolesSatu role.
GET /permissionssettings.rolesKatalog permission per kelompok menu; meta.grantable = yang boleh diberikan pengguna ini.
GET /audit-logssettings.auditAudit log terbaru dulu. Filter: category, user, from, to (YYYY-MM-DD, zona pembaca), q. Bawaan 50 per halaman.
GET /pipelinesleads.view / leads.manageWorkflow pipeline yang terlihat (admin, "lihat semua data", API key, dan pipeline.manage: semua; selain itu workflow tim pemanggil): tahap (kind new/progress/won/lost, label, warna, probability) dan tim Back Office yang menangani. meta = alasan kalah, kategori biaya, palet warna.
GET /pipelines/{id}leads.view / leads.manageSatu workflow (pemegang pipeline.manage juga mendapat jumlah lead per tahap).
GET /pipeline/settingsleads.view / leads.manageAlasan kalah, kategori biaya, palet warna tahap, dan batas tahap Proses.
GET /pipeline/dashboardleads.view / leads.manageDashboard pipeline: tiles (terbuka, perkiraan, baru 7 hari, menang/kalah, win rate, rata-rata hari closing, follow-up lewat), funnel, weekly (8 minggu), by_assignee, expenses, agenda hari ini, quota. Saringan pipeline_id, from/to (bawaan bulan berjalan), team_id, assignee_id. Hanya lead yang boleh dilihat.
GET /leadsleads.view / leads.manageDaftar lead (kepemilikan data: lead ber-PJ hanya untuk PJ-nya, tanpa PJ untuk tim lead; admin/API key semua). Saringan pipeline_id, stage_id, status (open/won/lost), team_id, assignee (me/none/id), client_id, contact_id, q (LEAD-…, judul, kontak, klien), followup (overdue/today/upcoming/none), created_from/created_to; sort & dir. meta.totals = jumlah, nilai prospek, nilai deal.
POST /leadsleads.manageBuat lead (create_lead; sumber api): pipeline_id, title, kontak (contact_id, atau contact {name, phone, email} dicocokkan lewat nomor HP lalu email, atau interaction_id), client_id/client_name, stage_id, team_id, assignee_id, value_estimate, budget, expected_close, followup_at, note, custom. Kuota lead bulanan berlaku (402).
GET /leads/boardleads.view / leads.managePapan kanban satu workflow (pipeline_id wajib): tahap + kartu lead terbuka & Menang/Kalah bulan berjalan (maks 100 per tahap, more), count/value per tahap, dan summary.
GET /leads/candidatesleads.view / leads.manageAnggota yang bisa menjadi PJ, dengan team_ids Back Office & is_admin.
GET /leads/duplicatesleads.manageLead terbuka kontak di workflow yang sama (contact_id, pipeline_id) — peringatan sebelum membuat lead.
GET /leads/{id}leads.view / leads.manageDetail lead: workflow & tahap, kontak, klien, percakapan asal, 30 aktivitas terbaru, checklist, jadwal, file, biaya, field kustom, dan can.
PATCH /leads/{id}leads.manageUbah title, client_id/client_name, value_estimate, value_won (lead Menang), budget, expected_close, followup_at/followup_note (null = hapus), team_id, assignee_id, lost_reason/lost_note (lead Kalah), custom. Hasil {"id", "visible": false} bila pemanggil tidak lagi boleh melihatnya.
POST /leads/{id}/moveleads.managePindah tahap: {"stage_id"}; ke Menang wajib value_won (bawaan nilai prospek), ke Kalah wajib lost_reason dari daftar (+ lost_note). Dari Menang/Kalah ke tahap Baru/Proses = buka lagi.
GET /leads/{id}/activitiesleads.view / leads.manageLinimasa lead berhalaman (before = id terakhir, kind=note untuk catatan saja).
POST /leads/{id}/activitiesleads.manageCatat aktivitas: {"kind": "call|chat|email|meeting|visit|note|other", "body", "happened_at", "duration_minutes"}. @Nama memberi notifikasi.
GET /lead-files/{id}leads.view / leads.manageFile lead (privat). ?download=1 untuk mengunduh.
GET /lead-expenses/{id}/receiptleads.view / leads.manageBukti biaya (privat). ?download=1 untuk mengunduh.
GET /interactions/{id}/leadsinteractions.viewTab Leads percakapan: SEMUA lead dari percakapan ini (ringkas, can_open) + lead terbuka lain kontaknya yang boleh dilihat.
GET /contacts/{id}/leadscontacts.viewLead kontak yang boleh dilihat (kosong tanpa akses Pipeline).
GET /clientscontacts.view / leads.viewMaster klien (tidak dibatasi tim): q, industry; setiap klien dengan contacts_count dan ringkasan lead yang boleh dilihat. meta.industries untuk saringan.
GET /clients/searchcontacts.view / leads.viewCari klien untuk pilihan di form: ?q=.
POST /clientsclients.manageTambah klien: {"name", "industry", "phone", "email", "website", "address", "city", "notes", "custom"}. Nama unik per workspace.
GET /clients/{id}contacts.view / leads.viewProfil klien, kontak tertaut, lead yang boleh dilihat, dan ringkasan deal.
PATCH /clients/{id}clients.manageUbah klien.
GET /segmentsbroadcasts.view / broadcasts.manageSegmen kontak: aturan, ringkasan, last_count, jumlah broadcast yang memakainya.
GET /segments/fieldsbroadcasts.view / broadcasts.manageKatalog field, operator, & pilihan nilai untuk aturan segmen (termasuk field kustom cf:{kunci}).
POST /segments/previewbroadcasts.view / broadcasts.managePratinjau aturan tanpa menyimpan: {"match": "all|any", "rules": [{"field", "op", "value", "value2"}]} → jumlah, punya nomor, berhenti berlangganan, contoh.
GET /segments/{id}broadcasts.view / broadcasts.manageDetail segmen + hitung ulang isinya (preview).
GET /broadcastsbroadcasts.view / broadcasts.manageDaftar broadcast (status, channel_id, q) dengan angka per status (stats).
GET /broadcasts/optionsbroadcasts.view / broadcasts.manageNomor (izin broadcast), segmen, pengaturan bawaan & batas aman, kuota, variabel pesan.
GET /broadcasts/dashboardbroadcasts.view / broadcasts.manageAngka periode (from, to, channel_id; maks 92 hari): corong, harian, per jam, alasan, pemakaian nomor hari ini, broadcast terbaru & terbaik.
GET /broadcasts/{id}broadcasts.view / broadcasts.manageDetail broadcast: audiens, pesan, lampiran, pengaturan, angka, alasan dilewati, persetujuan risiko, wait, can.
GET /broadcasts/{id}/recipientsbroadcasts.view / broadcasts.managePenerima & status per penerima (status bertingkat, q), termasuk isi pesan yang terkirim.
GET /broadcasts/{id}/exportbroadcasts.view / broadcasts.manageLaporan per penerima ?format=xlsx|csv.
GET /broadcasts/{id}/mediabroadcasts.view / broadcasts.manageLampiran broadcast (privat).
POST /broadcasts/{id}/estimatebroadcasts.view / broadcasts.managePerkiraan penerima (alasan dilewati), kuota, waktu selesai, dan tingkat risiko.
POST /broadcasts/{id}/previewbroadcasts.view / broadcasts.manageTeks jadi untuk maks 5 kontak contoh (variant, opsional text/footer).

Khusus aplikasi Onix

Endpoint berikut hanya menerima sesi aplikasi (cookie + X-CSRF-Token). Dengan API key dibalas 403 forbidden.

EndpointPermissionKeterangan
POST /wa-label-rulescontacts.manageBuat aturan label WhatsApp (Pro): action = contact_label (value = id label kontak), assign_team (id tim Frontline), atau solve. Free → 402.
PATCH /wa-label-rules/{id}contacts.manageUbah aturan (termasuk active).
DELETE /wa-label-rules/{id}contacts.manageHapus aturan.
POST /conversation-categoriescontacts.manageBuat kategori percakapan: {"name", "color", "parent_id"} — sampai 3 tingkat dan 200 kategori; nama unik di antara saudara.
PATCH /conversation-categories/{id}contacts.manageUbah nama, warna, urutan, atau induk kategori (parent_id, null = kategori utama; tidak ke dirinya/subkategorinya, maks 3 tingkat).
DELETE /conversation-categories/{id}contacts.manageHapus kategori; subkategori & percakapannya pindah ke induknya (tanpa kategori bila kategori utama).
PUT /kpi/configsettings.workspaceSimpan target workspace (targets) dan/atau target tim & orang (overrides, mengganti semuanya). Jam kerja kini diatur lewat PATCH /settings (business_hours di sini tetap diterima).
GET /kpi/exportreports.viewCSV KPI: type = summary, conversations, replies, atau tickets; user_id untuk satu orang (boleh untuk dirimu sendiri tanpa reports.view); channel_type seperti di atas. Data mentah punya kolom channel_type di akhir.
POST /kb/articles/{id}/embedkb.manageProses (ulang) artikel terbit untuk pencarian makna (Pro/Custom) — tombol Proses sekarang / Proses ulang / Coba lagi.
GET /reports/exportreports.viewUnduh CSV (UTF-8 + BOM): type = summary, daily, channel, label, category, agents, atau tickets, dengan rentang & filter yang sama seperti laporan JSON.
PATCH /me—Bahasa & zona waktu pribadi: {"locale": "id", "timezone": "Asia/Makassar"} ("" = ikut workspace).
POST /me/preferences—Preferensi pribadi, mis. status tur, sidebar, dan livechat_alert (0/1: bunyi & judul tab berkedip saat chat livechat baru menunggu).
GET /me/invitations—Undangan berlaku untuk email akun ini.
POST /me/invitations/{id}/accept—Terima undangan; workspace itu menjadi workspace aktif.
POST /me/workspace—Pindah workspace aktif: {"workspace_id": 3}.
GET /workspaces—Workspace tempat pengguna menjadi anggota aktif.
POST /workspaces—Buat workspace: {"name": "…", "timezone": "Asia/Jakarta"}. Akun Free/Pro memiliki satu workspace; pelanggan Custom sesuai jatah kontraknya (409). meta.create di GET /workspaces menunjukkan boleh/tidaknya.
GET /workspace/transfer—Pindah kepemilikan (khusus Owner): permintaan yang menunggu + calon penerima dan kelayakannya.
POST /workspace/transfer—Minta pindah kepemilikan: to_user_id, confirm_name (nama workspace), stay, stay_role_id, stay_team_ids.
DELETE /workspace/transfer—Batalkan permintaan yang menunggu.
GET /me/transfers—Permintaan pindah kepemilikan yang menunggu jawabanku.
GET /me/transfers/{id}—Detail permintaan (can_accept, becomes_free).
POST /me/transfers/{id}/accept—Terima: aku menjadi Owner; workspace itu menjadi workspace aktif.
POST /me/transfers/{id}/decline—Tolak permintaan.
PATCH /workspacesettings.workspaceGanti nama workspace.
POST /workspace/leave—Keluar dari workspace aktif (bukan Owner).
PATCH /settingssettings.workspaceUbah sebagian pengaturan General, mis. {"timezone": "Asia/Jakarta"} atau jam kerja workspace (semua paket) {"business_hours": {"enabled": true, "days": [{"day": 1, "open": true, "from": "08:00", "to": "17:00"}, …]}} — hari 1 = Senin.
GET /notifications—20 notifikasi terbaru dan jumlah yang belum dibaca.
POST /notifications/read—Tandai satu ({"id": 12}) atau semua notifikasi dibaca.
POST /realtime/token—Token WebSocket (Centrifugo) untuk pembaruan langsung; {"channels": ["conv:12", "ticket:5"]} untuk ikut kanal percakapan/tiket.
POST /interactions/{id}/readinteractions.viewTandai dibaca; tanda biru dikirim ke pelanggan.
POST /interactions/{id}/typinginteractions.replyIndikator mengetik: {"typing": true}.
POST /interactions/{id}/pickupinteractions.replyAmbil chat livechat yang belum diambil (atomik): yang kalah cepat mendapat 409 "Sudah diambil oleh {nama}". Pengunjung melihat "{nama} bergabung ke chat".
POST /interactions/{id}/historyinteractions.viewSalin riwayat lama dari HP untuk percakapan ini.
POST /channelschannels.manageTambah nomor WhatsApp: {"name": "CS Toko"} (kuota channel dicek). grant_selected_members: true = anggota yang aksesnya "channel tertentu" ikut diberi akses (juga di POST /channels/email, /livechat, /api).
GET /channels/{id}channels.manageDetail channel.
PATCH /channels/{id}channels.manageSemua jenis channel: name, jam kerja hours (objek seperti business_hours, atau null = ikuti jam kerja workspace), balasan otomatis auto_reply (greeting_on, greeting, away_on, away). WhatsApp: groups, reject_calls, call_reply, agent_initial (tambahkan inisial agen di akhir balasan). Pengaturan email/livechat/API lainnya lewat PATCH /channels/{id}/email, /livechat, /api.
DELETE /channels/{id}channels.manageHapus channel (diarsipkan bila sudah punya percakapan).
GET /channels/{id}/qrchannels.manageQR untuk scan (data URL PNG) + umur dalam detik.
POST /channels/{id}/pairchannels.manageKode pairing: {"phone": "0812…"}.
GET /channels/{id}/statuschannels.manageStatus terbaru nomor.
POST /channels/{id}/logoutchannels.manageLogout nomor dari Onix.
POST /channels/{id}/reconnectchannels.manageSambung ulang koneksi.
POST /channels/{id}/historychannels.manageImpor riwayat chat: {"days": 30} (7/30/90 hari).
GET /channels/{id}/labelschannels.manageLabel WhatsApp Business (dibaca dari HP).
POST /channels/email/detectchannels.manageTebak penyedia dari alamat: {"address": "[email protected]"} → provider, saran server IMAP/SMTP, dan petunjuk (mis. butuh App Password; Microsoft 365 belum didukung). Hanya sesi login.
POST /channels/email/testchannels.manageTes login IMAP & SMTP tanpa mengirim email: address, password, opsional username, imap_host/imap_port/imap_security (ssl/starttls), smtp_host/smtp_port/smtp_security, smtp_username/smtp_password. Hasil: folder yang bisa dipilih, folder Terkirim terdeteksi. Kesalahan dijelaskan per kolom (422).
POST /channels/emailchannels.manageSambungkan alamat email (kuota channel dicek; satu alamat hanya di satu workspace → 409): isian tes di atas + name, folders (selain Inbox), sent_folder, read_sent, save_sent, history_days (0/3/7/30, bawaan 7), sender_name, sender_with_agent, signature_html, ignore. Password disimpan terenkripsi dan tidak pernah dikembalikan.
PATCH /channels/{id}/emailchannels.manageUbah pengaturan email (kolom sama dengan di atas, kecuali history_days). Perubahan server, username, password, atau folder dites ulang sebelum disimpan.
GET /channels/{id}/email/folderschannels.manageDaftar folder kotak surat (login ulang) dengan tanda selected, plus folder Terkirim.
POST /channels/{id}/syncchannels.managePeriksa kotak surat sekarang (jeda setelah login gagal diabaikan). Hasil {"sync", "channel"}.
POST /channels/{id}/iconchannels.manageUnggah ikon/logo channel (multipart icon: PNG/JPG/WebP persegi, maks 512 KB). Livechat: logo yang sama tampil di kepala widget & peluncur.
DELETE /channels/{id}/iconchannels.manageKembali ke ikon bawaan jenis channel.
POST /channels/livechatchannels.manageBuat channel livechat (semua paket, satu slot channel): {"name": "Chat Website", "title", "color", "grant_selected_members"} → channel dengan livechat.key & livechat.embed_code. Lihat Livechat.
PATCH /channels/{id}/livechatchannels.managePengaturan widget: title, subtitle, color, font, radius, position, launcher_icon, intro, consent, start_label, attachments (off/images/files), email_fallback, allowed_domains; juga name, hours, auto_reply.
POST /channels/{id}/livechat/keychannels.manageGanti kunci publik; kode sematan lama berhenti bekerja.
POST /channels/apichannels.manageBuat channel API (Pro/Custom, satu slot channel): {"name", "webhook_url", "events", "retries", "grant_selected_members"} → credentials.key (hanya tampil sekali) & credentials.webhook_secret. Lihat Channel API.
GET /channels/{id}/apichannels.managePengaturan, endpoint pesan masuk, kunci aktif (awalan & terakhir dipakai), dan 50 pengiriman webhook terbaru.
PATCH /channels/{id}/apichannels.managewebhook_url (https, host publik), events (message.created selalu; conversation.updated opsional), retries (0–5); juga name, hours, auto_reply.
POST /channels/{id}/api/keychannels.manageGanti kunci channel; kunci lama langsung ditolak (401).
GET /channels/{id}/api/secretchannels.manageLihat secret webhook.
POST /channels/{id}/api/secretchannels.manageGanti secret webhook.
POST /channels/{id}/api/test-webhookchannels.manageKirim event ping sekarang: {"status", "http_status", "duration_ms", "error"}.
POST /channels/{id}/api/test-messagechannels.manageSimulasikan pesan masuk dari sistem Anda (tampil di Interaction).
POST /templatestemplates.manageBuat template: name, shortcut, category, body (JSON atau multipart dengan file).
PATCH /templates/{id}templates.manageUbah template.
DELETE /templates/{id}templates.manageHapus template.
POST /templates/{id}/attachmenttemplates.managePasang/ganti lampiran (multipart file).
DELETE /templates/{id}/attachmenttemplates.manageLepas lampiran.
GET /contacts/exportcontacts.manageEkspor kontak ke CSV (filter sama dengan daftar).
POST /contacts/importcontacts.manageImpor CSV (multipart file): hasil created, updated, skipped, errors.
DELETE /contacts/{id}contacts.manageHapus kontak beserta percakapan, pesan, media, catatan, dan linimasanya (tiket tetap ada tanpa tautan).
POST /contacts/{id}/mergecontacts.manageGabung kontak ganda: {"source_id": 9} dilebur ke kontak {id}.
POST /labelscontacts.manageBuat label: {"name": "VIP", "color": "orange"}.
PATCH /labels/{id}contacts.manageUbah nama, warna, atau urutan label.
DELETE /labels/{id}contacts.manageHapus label (dilepas dari semua kontak).
POST /custom-fieldscontacts.manageBuat field kustom kontak, interaction, atau tiket: {"entity": "interaction", "label": "Nomor invoice", "type": "text"}. Kunci unik per entitas.
PATCH /custom-fields/{id}contacts.manageUbah nama, pilihan, atau urutan field (entitas, jenis, & kunci tetap).
DELETE /custom-fields/{id}contacts.manageHapus field kustom; nilainya tidak lagi tampil.
POST /contact-fieldscontacts.manageBuat field kustom kontak: {"label": "Ukuran", "type": "select", "options": ["S", "M", "L"]}.
PATCH /contact-fields/{id}contacts.manageUbah nama, pilihan, atau urutan field.
DELETE /contact-fields/{id}contacts.manageHapus field kustom.
PATCH /members/{id}settings.membersUbah role, team_ids, view_all, akses channel, atau status (active/disabled). Role bukan admin wajib minimal satu tim. reply_initial (maks 20 karakter, kosong = otomatis dari nama, mis. ^BS) boleh diatur untuk semua anggota termasuk Owner.
POST /invitationssettings.membersUndang anggota: email, role_id, team_ids, view_all, channel_access, channel_ids.
POST /teamssettings.membersBuat tim: {"name": "Gudang", "type": "backoffice", "member_ids": [5, 7]}.
PATCH /teams/{id}settings.membersUbah nama, deskripsi, atau anggota tim (member_ids = daftar lengkap). Jenis tidak bisa diubah.
DELETE /teams/{id}settings.membersHapus tim; bila masih punya anggota/data (termasuk artikel KB khusus tim) wajib ?replacement_id= tim sejenis.
POST /invitations/{id}/resendsettings.membersKirim ulang undangan dengan tautan baru.
DELETE /invitations/{id}settings.membersBatalkan undangan.
POST /rolessettings.rolesBuat role: name, permissions, dan is_admin (hanya Owner/admin).
PATCH /roles/{id}settings.rolesUbah nama, permission, dan tanda is_admin role (kecuali Owner).
DELETE /roles/{id}settings.rolesHapus role yang tidak dipakai siapa pun.
POST /roles/{id}/duplicatesettings.rolesSalin role.
GET /api-keyssettings.apiAPI key aktif, tanpa nilai rahasianya.
POST /api-keyssettings.apiBuat key: {"name": "…", "scope": "read"}. Nilai utuh hanya dikembalikan sekali.
DELETE /api-keys/{id}settings.apiCabut key.
GET /billingsettings.billingPaket, pemakaian, invoice, dan profil tagihan.
POST /billing/upgradesettings.billingTerbitkan/pakai invoice upgrade lalu buat checkout Sassly Pay.
GET /billing/invoices/{number}settings.billingDetail invoice beserta riwayat percobaan bayar.
POST /billing/invoices/{number}/paysettings.billingAlamat checkout Sassly Pay (percobaan yang masih berlaku dipakai ulang).
PATCH /billing/profilesettings.billingProfil tagihan untuk invoice berikutnya.
POST /billing/downgrade-choicesettings.billingPilih channel & anggota yang tetap aktif setelah turun ke Free.
GET /billing/returnsettings.billingStatus pembayaran setelah kembali dari checkout (dicek ke Sassly Pay di server).
POST /pipelinespipeline.manageBuat workflow: {"name", "description", "team_ids"} (tim Back Office); tanpa stages → tahap bawaan.
PATCH /pipelines/{id}pipeline.manageUbah nama, deskripsi, tim, urutan; {"archived": false} memulihkan.
PUT /pipelines/{id}/stagespipeline.manageSimpan susunan tahap lengkap: satu Baru, 0–10 Proses, satu Menang, satu Kalah (label, warna, peluang %).
DELETE /pipelines/{id}pipeline.manageHapus workflow tanpa lead; yang masih punya lead diarsipkan.
PUT /pipeline/settingspipeline.manageSimpan lost_reasons & expense_categories.
GET /pipeline/exportreports.viewCSV raw data: type = leads (semua kolom + field kustom), activities, atau expenses; saringan pipeline_id, status, from/to.
GET /leads/import/templateleads.manageTemplate impor ?format=xlsx|csv.
POST /leads/import/previewleads.manageBaca file Excel/CSV (multipart file): kolom, contoh isi, saran pemetaan, token 1 jam.
POST /leads/importleads.managePeriksa (dry_run) atau jalankan impor dengan pemetaan kolom (kolom tanpa padanan → field kustom baru).
POST /leads/bulkleads.manageAksi massal (maks 100): move, assign, followup.
DELETE /leads/{id}leads.manageHapus lead (admin, PJ, atau pembuat); kuota tidak kembali.
PATCH /lead-activities/{id}leads.manageUbah aktivitas manual (penulis atau admin).
DELETE /lead-activities/{id}leads.manageHapus aktivitas manual (penulis atau admin).
POST /leads/{id}/filesleads.manageUnggah file lead (multipart file).
DELETE /lead-files/{id}leads.manageHapus file (pengunggah, PJ, atau admin).
POST /leads/{id}/checklistleads.manageTambah butir checklist {"text"}; PATCH /lead-checklist/{id} (text, done, position) dan DELETE /lead-checklist/{id}.
PATCH /lead-checklist/{id}leads.manageUbah / centang butir checklist.
DELETE /lead-checklist/{id}leads.manageHapus butir checklist.
POST /leads/{id}/eventsleads.manageTambah jadwal: {"kind", "title", "starts_at", "ends_at", "location", "notes", "remind_minutes"}.
PATCH /lead-events/{id}leads.manageUbah jadwal (mengubah waktu mulai mengatur ulang pengingat).
DELETE /lead-events/{id}leads.manageHapus jadwal.
POST /leads/{id}/expensesleads.manageCatat biaya (multipart): spent_on, category, amount, note, spent_by, receipt (foto/PDF).
PATCH /lead-expenses/{id}leads.manageUbah biaya (pencatat, pembayar, atau admin).
DELETE /lead-expenses/{id}leads.manageHapus biaya beserta buktinya.
DELETE /clients/{id}clients.manageHapus klien; kontak & lead tetap ada tanpa tautan klien.
POST /clients/{id}/contactsclients.manageTautkan ({"contact_id", "link": true}) atau lepas kontak; lead kontak yang belum berklien ikut tertaut.
POST /contacts/{id}/broadcast-opt-outcontacts.manage / broadcasts.manageBerhenti ({"opt_out": true}) atau kembali berlangganan broadcast.
POST /segmentsbroadcasts.manageBuat segmen: {"name", "description", "match", "rules"} (maks 15 aturan, nama unik).
PATCH /segments/{id}broadcasts.manageUbah segmen.
DELETE /segments/{id}broadcasts.manageHapus segmen (ditolak bila dipakai broadcast yang terjadwal/berjalan/dijeda).
GET /segments/{id}/exportbroadcasts.manageUnduh isi segmen (CSV).
POST /broadcastsbroadcasts.manageBuat draf (Pro/Custom): {"name", "channel_id", "audience", "messages", "settings"}.
PATCH /broadcasts/{id}broadcasts.manageUbah draf (bagian yang dikirim saja).
DELETE /broadcasts/{id}broadcasts.manageHapus draf, broadcast selesai, atau dibatalkan.
POST /broadcasts/{id}/mediabroadcasts.managePasang lampiran (multipart file: gambar, video, dokumen); DELETE untuk melepas.
POST /broadcasts/{id}/testbroadcasts.manageKirim uji ke satu nomor {"phone", "variant"} (tidak memakai kuota, dibatasi per jam).
POST /broadcasts/{id}/schedulebroadcasts.manageMulai sekarang/jadwalkan dengan {"risk_ack": true}; daftar penerima dikunci.
POST /broadcasts/{id}/pausebroadcasts.manageJeda. Juga /resume, /cancel, /retry (kirim ulang yang gagal), /unschedule (kembali ke draf), /duplicate.

Publik

EndpointPermissionKeterangan
GET /public/config—Harga & kuota paket, pajak, dan aturan retensi (mengikuti X-Locale).
GET /public/demo-livechat—Kunci livechat data contoh untuk halaman /demo/livechat (null bila belum ada).
POST /plan-requests—Permintaan paket Custom: name, email, message, opsional company, phone, channels, agents.
GET /invite/{token}—Ringkasan undangan dari tautan email.

Widget livechat (publik)

Dipanggil widget livechat (pemuat livechat.js & iframe Onix) tanpa login. {key} = kunci publik channel. Selain config dan logo, semua memakai token pengunjung di Authorization: Bearer. Penjelasan lengkap: Livechat.

EndpointAksesKeterangan
GET /livechat/{key}/config—Tampilan & teks widget, status jam kerja, token formulir (CORS *).
GET /livechat/{key}/logo—Logo widget.
POST /livechat/{key}/visitors—Mulai chat: name, email, phone (wajib), page_url, locale, form_token → token pengunjung.
GET /livechat/{key}/conversation—Percakapan pengunjung + pesan sesudah ?after={id}, posisi antrian, agen.
POST /livechat/{key}/messages—Kirim pesan: text, client_id (kirim ulang aman), opsional lampiran multipart file.
GET /livechat/{key}/files/{id}—Lampiran di percakapan pengunjung itu saja.
POST /livechat/{key}/realtime—Token WebSocket kanal visitor:{sesi}.
POST /livechat/{key}/typing—Pengunjung sedang mengetik.
POST /livechat/{key}/seen—Widget terbuka & pesan sampai message_id sudah dilihat.

Channel API (kunci channel)

Untuk sistem Anda yang mengirim pesan pelanggan ke Onix lewat channel API, dengan kunci channel onx_ch_… di Authorization: Bearer (bukan API key workspace). Balasan agen dikirim ke webhook Anda. Panduan, contoh payload, dan verifikasi tanda tangan: Channel API.

EndpointAksesKeterangan
POST /channel-api/messages—Pesan masuk: {"conversation_id", "contact": {"id", "name", "email", "phone"}, "message": {"id", "text", "attachments", "sent_at"}}. message.id unik → kirim ulang aman.
POST /channel-api/messages/{id}/status—Tandai balasan agen delivered/read.
GET /channel-api/files/{token}—Lampiran balasan agen (tautan bertanda tangan di webhook, 7 hari).

Contoh

Siapa pemilik key ini

curl -s "https://onix.sassly.ai/api/v1/me" \
  -H "Authorization: Bearer $ONIX_API_KEY"
{
  "data": {
    "user": {"id": 12, "name": "Budi Santoso", "email": "[email protected]", "avatar_url": null, "locale": "id", "timezone": null, "last_login_at": "2026-10-08T02:00:00Z"},
    "via": "key",
    "timezone": "Asia/Jakarta",
    "workspace": {
      "id": 3, "name": "Toko Budi", "slug": "toko-budi", "plan": "pro", "pro_until": "2026-11-08T03:15:00Z", "in_grace": false, "timezone": "Asia/Jakarta",
      "limits": {"channels": 4, "users": 10, "tickets_per_month": 200, "replies_per_day": 0, "ai_per_month": 2000, "ai": true, "api": true},
      "created_at": "2026-10-08T02:00:00Z"
    },
    "role": {"id": 1, "name": "Owner"},
    "is_owner": true,
    "is_platform_admin": false,
    "permissions": ["dashboard.view", "interactions.view", "interactions.reply", "interactions.assign", "templates.manage", "channels.manage", "settings.members", "settings.roles", "settings.billing", "settings.audit", "settings.workspace", "settings.api"],
    "channel_ids": null,
    "realtime": true
  }
}

Pada limits, nilai 0 berarti tanpa batas. channel_ids bernilai null berarti akses ke semua channel. timezone adalah zona efektif (zona pribadi pengguna, atau zona workspace).

Membalas pelanggan WhatsApp

Cari percakapan yang belum dibalas, lalu kirim balasan (butuh key ber-scope write). Pesan dikirim lewat nomor WhatsApp percakapan itu dan tercatat atas nama key.

curl -s "https://onix.sassly.ai/api/v1/interactions?tab=unreplied&per_page=5" \
  -H "Authorization: Bearer $ONIX_API_KEY"
curl -s -X POST "https://onix.sassly.ai/api/v1/interactions/42/messages" \
  -H "Authorization: Bearer $ONIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Halo kak, pesanan sudah kami kirim hari ini 🙏"}'
{
  "data": {
    "id": 981, "interaction_id": 42, "direction": "out", "type": "text", "body": "Halo kak, pesanan sudah kami kirim hari ini 🙏",
    "media": null, "reply_to": null, "sender": {"name": "API · CRM", "user_id": 12, "jid": null},
    "status": "sent", "error": null, "reactions": [], "sent_at": "2026-10-09T03:15:00Z", "created_at": "2026-10-09T03:15:00Z"
  },
  "meta": {"replies": {"used": 1, "limit": 0}}
}

Pesan yang ditolak WhatsApp tetap tersimpan dengan "status": "failed" dan alasan di error; kirim ulang lewat /retry. Maksimal 30 pesan per menit per nomor (429 bila lewat). Lampiran: kirim multipart/form-data dengan field file (maks. 16 MB).

Audit log anggota sejak awal bulan

curl -s "https://onix.sassly.ai/api/v1/audit-logs?category=anggota&from=2026-10-01&per_page=20" \
  -H "Authorization: Bearer $ONIX_API_KEY"
{
  "data": [
    {
      "id": 41, "action": "member.invited", "label": "Mengundang anggota", "category": "anggota", "icon": "user-plus",
      "target": "[email protected]", "details": "Role: Agent",
      "user": {"id": 12, "name": "Budi Santoso", "avatar_url": null},
      "ip": "203.0.113.10", "created_at": "2026-10-08T03:20:00Z"
    }
  ],
  "meta": {"page": 1, "per_page": 20, "total": 1, "pages": 1, "categories": [{"key": "anggota", "label": "Anggota & undangan"}, "…"], "users": [{"id": 12, "name": "Budi Santoso"}]}
}

Contoh JavaScript (Node 18+) dan PHP

const res = await fetch("https://onix.sassly.ai/api/v1/members", {
  headers: { Authorization: `Bearer ${process.env.ONIX_API_KEY}` },
});
const { data, error } = await res.json();
if (error) throw new Error(`${error.code}: ${error.message}`);
$ch = curl_init('https://onix.sassly.ai/api/v1/roles');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('ONIX_API_KEY')],
]);
$body = json_decode(curl_exec($ch), true);

Event real-time

Aplikasi menerima event lewat WebSocket (Centrifugo) di kanal ws:{workspace_id} dan user:{user_id}. Isi setiap event: {"event": "…", "data": {…}, "at": "…Z"}.

EventKanalKapan
notification.createduser:{id}Notifikasi baru untuk pengguna.
workspace.updatedws:{id}Nama workspace berubah.
settings.updatedws:{id}Pengaturan General berubah (mis. zona waktu).
members.changedws:{id}Anggota/undangan berubah.
teams.changedws:{id}Tim atau anggota tim berubah.
billing.updatedws:{id}Paket berubah (Pro aktif, turun ke Free, diatur admin).
inbox.changedws:{id}Ada perubahan di inbox: {"interaction_id", "channel_id", "reason"} tanpa isi pesan — muat ulang daftar lewat API (akses channel tetap dicek).
channel.updatedws:{id}Status/pengaturan channel berubah, termasuk progres impor riwayat.
labels.changedws:{id}Label WhatsApp Business sebuah channel berubah.
message.created / message.updatedconv:{id}Pesan baru atau berubah (status kirim, reaksi, edit, tarik, media selesai diunduh). Isi = resource pesan.
interaction.updatedconv:{id}Status, penugasan, atau ringkasan percakapan berubah.
typingconv:{id}Pelanggan (WhatsApp/livechat) atau agen lain sedang mengetik.
visitor.onlineconv:{id}Pengunjung livechat sedang membuka widget (dikirim widget ±30 detik sekali).
tickets.changedws:{id}Tiket dibuat/berubah: {"ticket_id", "reason"} tanpa isi — muat ulang lewat API (hak lihat tetap dicek).
ticket.message / ticket.message.updatedticket:{id}Pesan diskusi baru atau berubah (dikirim sebagai feedback). Isi = resource pesan tiket.
ticket.eventticket:{id}Log tiket baru (status, prioritas, tenggat, peserta, feedback, kesimpulan).
ticket.updatedticket:{id}Data tiket berubah — muat ulang detailnya.
contacts.changedws:{id}Kontak dibuat/diubah/digabung/dihapus/diimpor: {"contact_id", "reason"}.
contacts.settingsws:{id}Label Onix atau field kustom berubah.
kb.changedws:{id}Kategori atau artikel knowledge base berubah, termasuk selesai/gagal diproses untuk pencarian makna: {"article_id"} atau {"category_id"}.
pipeline.changedws:{id}Lead, workflow, atau pengaturan Pipeline berubah: {"lead_id", "pipeline_id", "reason"} tanpa isi — muat ulang lewat API (kepemilikan data tetap dicek).
clients.changedws:{id}Klien dibuat/diubah/dihapus atau kontak ditautkan: {"client_id"}.
broadcasts.changedws:{id}Broadcast dibuat/diubah/dimulai/dijeda/selesai, progres kirim, atau tanda terima/balasan penerima: {"broadcast_id", "reason"} — muat ulang lewat API.
segments.changedws:{id}Segmen dibuat/diubah/dihapus: {"segment_id"}.

Kanal conv:{id} hanya diberikan bila pengguna boleh melihat percakapan itu (akses channel + aturan tim). Saat percakapan di-assign ke tim/orang lain, yang kehilangan akses otomatis dicabut dari kanalnya.

Kanal ticket:{id} hanya diberikan bila pengguna boleh membuka tiket itu (peserta, anggota tim untuk tiket tanpa penanggung jawab, atau melihat semua data).

Widget livechat memakai kanal terpisah visitor:{sesi} (token dari POST /livechat/{key}/realtime) dengan event message, queue, typing, dan read — lihat Livechat.