Zena APIDokumentasi

Dokumentasi Zena API

Kirim pesan WhatsApp, SMS, Email, Telegram, Line, dan Facebook lewat satu API.

VersiBase URLUntuk
v1https://api.zena.co.id/v1Format Sandeza. Untuk sistem yang sudah memakai Sandeza: cukup ganti base URL.
v2https://api.zena.co.id/v2API platform Zena dengan kunci API b1k_…. Untuk integrasi baru. Lihat API v2.

Semua alamat memakai HTTPS. Request dan jawaban berformat JSON (UTF-8).

Migrasi dari Sandeza

  1. Ganti Base URL Send menjadi https://api.zena.co.id/v1. Path, isi request, dan bentuk jawaban tetap sama:
    POST https://api.zena.co.id/v1/json/send/cgw
  2. Pakai username dan password baru dari Zena untuk membuat signature. Cara membuatnya sama seperti sebelumnya.
  3. Pastikan setiap sender_id yang Anda pakai sudah didaftarkan oleh Zena. Sender ID yang belum terdaftar dijawab dengan RC 8.
  4. Kirimkan ke Zena alamat endpoint Anda untuk menerima status pengiriman (DR) dan pesan masuk (MO). Format keduanya sama seperti sebelumnya.

Hal kecil yang berbeda dirangkum di Perbedaan dari Sandeza.

Autentikasi

Setiap request membawa tiga field berikut di body:

FieldIsi
usernameUsername dari Zena.
timeWaktu saat request dibuat, Unix timestamp dalam detik (string). Selisihnya dengan jam server Zena paling banyak 5 menit; lebih dari itu dijawab RC 11.
signatureSHA-256 (hex) dari username + password + time, digabung tanpa pemisah.

Contoh membuat signature:

// PHP
$time = (string) time();
$signature = hash('sha256', $username . $password . $time);
// Node.js
const time = String(Math.floor(Date.now() / 1000));
const signature = require("crypto").createHash("sha256").update(username + password + time).digest("hex");
# Python
import hashlib, time as t
ts = str(int(t.time()))
signature = hashlib.sha256((username + password + ts).encode()).hexdigest()

Jika Zena mengaktifkan daftar IP untuk akun Anda, request dari IP lain dijawab RC 6.

Mengirim pesan

POST https://api.zena.co.id/v1/json/send/cgw
Content-Type: application/json
FieldWajibKeterangan
signatureyaLihat Autentikasi.
timeyaUnix timestamp (detik).
usernameyaUsername dari Zena.
typetidak"2" = satu channel atau beberapa channel sekaligus (bawaan). "1" = utama + cadangan, lihat type 1.
ref_idyaID unik buatan Anda, maksimal 50 karakter. ID yang sudah pernah diterima dijawab RC 9. Request yang ditolak (RC selain 0) tidak menyimpan ref_id-nya, jadi boleh dikirim ulang.
subjecttidakDipakai sebagai subjek email.
sender_idyaSender ID yang didaftarkan Zena untuk akun Anda.
budget_codetidakDiterima, tidak dipakai.
created_bytidakDiterima, tidak dipakai.
channelyaObjek berisi satu atau lebih channel: sms, email, telegram, line, facebook, whatsapp.

Contoh lengkap dengan curl:

curl -X POST https://api.zena.co.id/v1/json/send/cgw \
  -H "Content-Type: application/json" \
  -d '{"signature":"<sha256 hex>","time":"1730458063","type":"2","username":"toko_maju","ref_id":"inv-2024-000123","subject":"Notifikasi pesanan","sender_id":"TOKOMAJU","channel":{"sms":{"message":"Pesanan Anda sudah dikirim.","msisdn":"081234567890"}}}'

Nomor tujuan (msisdn) boleh ditulis 0812…, 62812…, atau +62812….

SMS

FieldWajibKeterangan
messageyaIsi pesan.
msisdnyaNomor tujuan.
backup_ontidakHanya untuk type 1. Lihat cadangan.
backup_exptidakHanya untuk type 1.
use_backup_sendertidakDiterima, tidak dipakai.
{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "sms": {
      "message": "Kode OTP Anda 482910. Jangan berikan ke siapa pun.",
      "msisdn": "081234567890"
    }
  }
}

Email

FieldWajibKeterangan
emailyaAlamat tujuan.
messageya*Isi email (teks). Untuk template: nilai isian, "nama:Budi;nomor:INV-01".
template_idtidakID template email yang didaftarkan Zena untuk akun Anda. Isian {nama} di template diganti dengan nilai dari message.
attachmenttidakAlamat https file, dipisah ;. Maksimal 5 MB per file.

Subjek diambil dari subject request, atau dari template jika memakai template_id.

{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "email": {
      "email": "budi@contoh.co.id",
      "message": "nama:Budi;nomor:INV-2024-000123",
      "template_id": "invoice"
    }
  }
}

Jika ada lebih dari satu lampiran, lampiran pertama dikirim bersama isi email. Lampiran berikutnya dikirim sebagai email tersendiri dengan subjek yang sama.

Telegram, Line, Facebook

FieldWajibKeterangan
chat_idyaID penerima di platform itu.
messageya*Isi pesan. Boleh kosong jika ada lampiran.
attachmenttidakAlamat https file, dipisah ;. Setiap file dikirim sebagai pesan tersendiri setelah teks.
{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "telegram": {
      "message": "Pesanan INV-2024-000123 sudah dikirim.",
      "chat_id": "396731234"
    }
  }
}

Di ketiga platform ini, bisnis hanya bisa mengirim ke orang yang sudah pernah mengirim pesan lebih dulu ke akun bisnis tersebut. chat_id didapat dari pesan masuk.

WhatsApp: template

Template harus sudah disetujui WhatsApp di akun Anda. Bahasa template dipilih otomatis.

FieldWajibKeterangan
msisdnyaNomor tujuan.
template_idyaNama template. Template yang tidak ada dijawab RC 2.
headertidakIsian header: teks, gambar, video, dokumen, atau lokasi.
messagetidakIsian body ({{1}}, {{2}}, …). Kosongkan untuk template tanpa isian.
buttontidakIsian tombol: URL dinamis, OTP, katalog.
carouseltidakKartu-kartu template carousel.
use_mm_litetidakUntuk template kategori UTILITY atau AUTHENTICATION dijawab RC 25. Untuk template MARKETING, pesan dikirim sebagai template biasa.

Body dan header teks

{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "whatsapp": {
      "msisdn": "081234567890",
      "template_id": "konfirmasi_pesanan",
      "header": {
        "type": "header",
        "parameters": [
          {
            "type": "text",
            "text": "Toko Maju"
          }
        ]
      },
      "message": {
        "type": "body",
        "parameters": [
          {
            "type": "text",
            "text": "Budi"
          },
          {
            "type": "text",
            "text": "INV-2024-000123"
          }
        ]
      }
    }
  }
}

Header gambar, video, dokumen

{
  "header": {
    "type": "header",
    "parameters": [
      {
        "type": "image",
        "image": {
          "link": "https://contoh.co.id/promo.png"
        }
      }
    ]
  }
}

{
  "header": {
    "type": "header",
    "parameters": [
      {
        "type": "document",
        "document": {
          "link": "https://contoh.co.id/invoice.pdf",
          "filename": "Invoice.pdf"
        }
      }
    ]
  }
}

Untuk video, ganti image dengan video. File harus bisa diunduh lewat https.

Header lokasi

{
  "header": {
    "type": "header",
    "parameters": [
      {
        "type": "location",
        "location": {
          "latitude": "-6.1754",
          "longitude": "106.8272",
          "name": "Toko Maju Pusat",
          "address": "Jl. Merdeka No. 1, Jakarta"
        }
      }
    ]
  }
}

Tombol URL dinamis dan OTP

{
  "button": [
    {
      "type": "BUTTON",
      "sub_type": "URL",
      "index": "0",
      "parameters": [
        {
          "type": "text",
          "text": "pesanan/INV-2024-000123"
        }
      ]
    }
  ]
}

Untuk template OTP (kategori AUTHENTICATION), isi kode yang sama di message dan di tombol:

{
  "template_id": "kode_otp",
  "message": {
    "type": "body",
    "parameters": [
      {
        "type": "text",
        "text": "482910"
      }
    ]
  },
  "button": [
    {
      "type": "BUTTON",
      "sub_type": "URL",
      "index": "0",
      "parameters": [
        {
          "type": "text",
          "text": "482910"
        }
      ]
    }
  ]
}

Katalog

{
  "button": [
    {
      "type": "BUTTON",
      "sub_type": "catalog",
      "index": "0",
      "parameters": [
        {
          "type": "action",
          "action": {
            "thumbnail_product_retailer_id": "SKU-001"
          }
        }
      ]
    }
  ]
}

Carousel

{
  "template_id": "promo_carousel",
  "header": "",
  "button": "",
  "message": {
    "type": "BODY",
    "parameters": [
      {
        "type": "TEXT",
        "text": "20OFF"
      }
    ]
  },
  "carousel": {
    "type": "CAROUSEL",
    "cards": [
      {
        "card_index": 0,
        "components": [
          {
            "type": "HEADER",
            "parameters": [
              {
                "type": "IMAGE",
                "image": {
                  "link": "https://contoh.co.id/kartu1.png"
                }
              }
            ]
          },
          {
            "type": "BODY",
            "parameters": [
              {
                "type": "TEXT",
                "text": "10%"
              }
            ]
          },
          {
            "type": "BUTTON",
            "sub_type": "QUICK_REPLY",
            "index": "0",
            "parameters": [
              {
                "type": "PAYLOAD",
                "payload": "beli-1"
              }
            ]
          },
          {
            "type": "BUTTON",
            "sub_type": "URL",
            "index": "1",
            "parameters": [
              {
                "type": "payload",
                "payload": "promo/kartu1"
              }
            ]
          }
        ]
      }
    ]
  }
}

Huruf besar atau kecil pada type dan sub_type sama saja.

WhatsApp: format delimiter

Bentuk ringkas untuk template. Bagian dipisah :=:, beberapa isian dipisah ||.

FieldContoh
messagetext:=:Budi||text:=:INV-2024-000123
header tekstext:=:Toko Maju
header gambar / video / dokumenimage:=:https://contoh.co.id/promo.png
document:=:https://contoh.co.id/invoice.pdf:=:Invoice.pdf
header lokasilocation:=:-6.1754:=:106.8272:=:Toko Maju Pusat:=:Jl. Merdeka No. 1
button URL / OTPbutton:=:url:=:pesanan/INV-2024-000123
button quick replybutton:=:quick_reply:=:ya

Beberapa tombol dipisah || dan diberi nomor urut 0, 1, 2, … sesuai urutannya.

{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "whatsapp": {
      "msisdn": "081234567890",
      "template_id": "konfirmasi_pesanan",
      "header": "text:=:Toko Maju",
      "message": "text:=:Budi||text:=:INV-2024-000123"
    }
  }
}

WhatsApp: tanpa template

Pesan tanpa template hanya bisa dikirim dalam 24 jam sejak pelanggan terakhir mengirim pesan. Di luar itu, request dijawab RC 1, dan Anda perlu memakai template.

message.typeIsi
textbody.text
image, video, audio, documentbody.media.url (https). body.text (opsional) menjadi keterangan gambar/video/dokumen.
buttonbody.text, body.button (maksimal 3: key, title maks. 20 karakter), header (opsional: teks, atau gambar/video/dokumen lewat media_url), footer.text (opsional).
listbody.text, body.list.title (label tombol pembuka), body.list.sections[].title, body.list.sections[].actions[] (key, title, description). Maksimal 10 baris. Header hanya teks.
locationbody.location.latitude, longitude, name, address
{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "whatsapp": {
      "msisdn": "081234567890",
      "message": {
        "type": "text",
        "body": {
          "text": "Halo Budi, pesanan Anda sedang dikemas."
        }
      }
    }
  }
}
{
  "whatsapp": {
    "msisdn": "081234567890",
    "message": {
      "type": "button",
      "header": {
        "type": "text",
        "text": "Konfirmasi"
      },
      "body": {
        "text": "Apakah pesanan sudah diterima?",
        "button": [
          {
            "type": "postback",
            "key": "sudah",
            "title": "Sudah"
          },
          {
            "type": "postback",
            "key": "belum",
            "title": "Belum"
          }
        ]
      },
      "footer": {
        "text": "Toko Maju"
      }
    }
  }
}
{
  "whatsapp": {
    "msisdn": "081234567890",
    "message": {
      "type": "list",
      "body": {
        "text": "Pilih paket langganan",
        "list": {
          "title": "Lihat paket",
          "sections": [
            {
              "title": "Paket",
              "actions": [
                {
                  "type": "postback",
                  "key": "basic",
                  "title": "Basic",
                  "description": "Rp 99.000/bulan"
                },
                {
                  "type": "postback",
                  "key": "pro",
                  "title": "Pro"
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Tombol atau baris list yang dipilih pelanggan kembali sebagai pesan masuk dengan message.type = "postback" dan postback.key.

Channel whatsapp_generic dengan message berupa teks biasa juga diterima, dan dikirim sebagai pesan teks WhatsApp.

Beberapa channel sekaligus

Dengan type: "2", setiap channel di dalam channel dikirim bersamaan. Satu ref_id berlaku untuk semuanya. DR membedakannya lewat field channel.

{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "2",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "sms": {
      "message": "Pesanan INV-2024-000123 sudah dikirim.",
      "msisdn": "081234567890"
    },
    "email": {
      "email": "budi@contoh.co.id",
      "message": "Pesanan INV-2024-000123 sudah dikirim."
    }
  }
}
  • Semua channel diperiksa dulu sebelum ada yang dikirim. Jika ada satu yang tidak valid, seluruh request ditolak.
  • Jika setidaknya satu channel terkirim, jawabannya RC 0. Channel yang ditolak dilaporkan lewat DR dengan status 4.
  • Satu jenis channel hanya boleh muncul sekali dalam satu request.

Utama + cadangan (type 1)

Dengan type: "1", channel pertama di channel menjadi channel utama. Channel berikutnya baru dikirim jika channel sebelumnya memenuhi aturan backup_on miliknya.

Kode backup_onCadangan dikirim jika pesan…
1tidak terkirim (ditolak sebelum keluar)
2tidak sampai (undelivered)
3masih tertunda setelah sekian menit
4gagal
5, 6, 7Email: spam, bounced, blacklist (dibaca sebagai gagal)
9WhatsApp: sudah sampai tetapi belum dibaca setelah sekian menit

backup_on berisi kode yang dipisah ;. backup_exp berisi menit tunggu dengan urutan yang sama. Untuk selain kode 3 dan 9, isi 0.

{
  "signature": "<sha256 hex>",
  "time": "1730458063",
  "type": "1",
  "username": "toko_maju",
  "ref_id": "inv-2024-000123",
  "subject": "Notifikasi pesanan",
  "sender_id": "TOKOMAJU",
  "channel": {
    "sms": {
      "message": "Kode OTP Anda 482910.",
      "msisdn": "081234567890",
      "backup_on": "1;2;3",
      "backup_exp": "0;0;10"
    },
    "telegram": {
      "message": "Kode OTP Anda 482910.",
      "chat_id": "396731234"
    }
  }
}

Contoh di atas: SMS dikirim lebih dulu. Telegram dikirim jika SMS ditolak, tidak sampai, atau belum sampai setelah 10 menit. Jika SMS sampai, Telegram tidak dikirim dan DR-nya berstatus 14.

Jika backup_on kosong, cadangan dikirim saat pesan ditolak atau gagal (setara 1;2;4).

Jawaban & kode RC

Setiap jawaban memakai HTTP 200. Hasilnya dibaca dari rc:

{
  "rc": "0",
  "ref_id": "inv-2024-000123",
  "code_sms": "wMjAyMTA0MTQwODA2MjMxNTg3MTQ4",
  "reason_status": ""
}

{
  "rc": "8",
  "ref_id": "inv-2024-000124",
  "code_sms": "",
  "reason_status": "Unknown Sender Id"
}
rcArti
0Diterima. code_sms adalah ID request di Zena.
1Parameter atau JSON tidak valid, jenis pesan tidak didukung, atau pesan bebas di luar 24 jam.
2Channel, template, atau konfigurasi tidak ditemukan; batas request terlampaui; atau gangguan sementara (boleh dicoba lagi).
3Nomor, alamat, atau chat_id tujuan tidak valid.
4Signature salah atau username tidak dikenal.
5Akun tidak aktif.
6IP tidak ada di daftar yang diizinkan.
7Saldo tidak cukup.
8Sender ID belum terdaftar untuk channel itu.
9ref_id sudah pernah dipakai.
11time terlalu jauh dari jam server (lebih dari 5 menit).
24Pesan terlalu panjang (maksimal 4.096 karakter).
25use_mm_lite pada template UTILITY atau AUTHENTICATION.

Status pengiriman (DR)

Zena mengirim POST JSON ke alamat DR Anda setiap kali status pesan berubah. Balas dengan HTTP 2xx dalam 10 detik. Jika tidak, Zena mencoba lagi setelah 1 menit, 5 menit, 30 menit, 2 jam, dan 6 jam.

{
  "status": 1,
  "channel": "7",
  "ref_id": "inv-2024-000123",
  "time": "2026-11-14 19:12:52",
  "division_id": "b6de8c1d-8e1f-4b82-9148-0a7ab9577b73",
  "price": "PAID"
}
FieldIsi
statusLihat tabel status di bawah.
channel1 SMS · 2 Line · 3 Email · 4 Facebook · 5 Telegram · 7 WhatsApp
ref_idref_id dari request Anda.
timeWaktu perubahan status, WIB (YYYY-MM-DD HH:mm:ss).
division_idID akun Anda di Zena.
priceHanya WhatsApp: PAID untuk template, FREE untuk pesan bebas atau yang tidak terkirim.
counter_messageHanya SMS: jumlah bagian SMS.
statusArti
1Delivered: sampai di penerima
4Tidak terkirim: ditolak sebelum keluar (dalam request beberapa channel atau utama + cadangan)
5Failed: gagal
7Opened: email dibuka (jika dilaporkan oleh channel)
9Read: dibaca
14Cadangan tidak dipakai (type 1)

Satu pesan bisa menerima beberapa DR, misalnya 1 lalu 9. Status yang datang terlambat dan mundur, misalnya 1 setelah 9, tidak dikirim.

Pesan masuk (MO)

Pesan dari pelanggan ke channel yang terdaftar untuk akun Anda dikirim sebagai POST JSON ke alamat MO Anda, dengan aturan ulang yang sama seperti DR.

{
  "from": {
    "avatar": "",
    "description": "",
    "name": "Budi",
    "username": "6281234567890"
  },
  "message": {
    "id": "184467",
    "interactive": {
      "body": {
        "text": ""
      },
      "footer": {
        "text": "",
        "title": ""
      },
      "header": {
        "media_url": "",
        "text": "",
        "type": ""
      },
      "items": null
    },
    "location": {
      "address": "",
      "latitude": 0,
      "live_period": 0,
      "longitude": 0,
      "name": ""
    },
    "media": {
      "animated": false,
      "id": "",
      "mime_type": "",
      "name": "",
      "size": 0,
      "url": ""
    },
    "postback": {
      "key": "",
      "title": "",
      "type": "",
      "value": ""
    },
    "reply_id": "",
    "text": "Oke kak, terima kasih",
    "times": 1794630153,
    "timestamp": "2026-11-14T04:22:33Z",
    "type": "text"
  },
  "reply_token": ""
}
FieldIsi
from.usernameNomor WhatsApp atau chat_id pengirim. Pakai sebagai msisdn / chat_id untuk membalas.
from.nameNama pengirim, jika ada.
message.typetext, image, video, audio, document, voice, location, atau postback (tombol / baris list yang dipilih).
message.textIsi teks.
message.media.urlAlamat file untuk pesan gambar, video, audio, atau dokumen.
message.postbackUntuk postback: key = key tombol yang dipilih, title = labelnya.
message.times / timestampWaktu pesan: Unix detik / ISO 8601 UTC.

Perbedaan dari Sandeza

  • Kredensial: username dan password dari Zena, bukan yang lama.
  • Status "sent": tidak dikirim sebagai DR. DR dikirim saat pesan sampai, dibaca, gagal, tidak terkirim, atau saat cadangan tidak dipakai.
  • Field price: berdasarkan jenis pesan (template = PAID), bukan tagihan per pesan.
  • Template email: didaftarkan lewat Zena.
  • Field yang diterima tapi tidak dipakai: use_backup_sender, budget_code, created_by. use_mm_lite pada template MARKETING dikirim sebagai template biasa.
  • Tambahan: keterangan (caption) untuk media WhatsApp lewat body.text, beberapa tombol dalam format delimiter, dan tombol quick reply delimiter.
  • Jawaban reason_status bisa berisi penjelasan dalam bahasa Inggris yang lebih rinci daripada daftar RC.

Batasan

  • 120 request per menit per akun. Kelebihannya dijawab RC 2; coba lagi setelah satu menit.
  • Ukuran body request maksimal 1 MB. File dikirim sebagai alamat https, bukan isi file.
  • Pesan teks maksimal 4.096 karakter.
  • Tombol WhatsApp maksimal 3, label maksimal 20 karakter. List maksimal 10 baris.

API v2

Untuk integrasi baru, API v2 menyediakan API platform Zena secara langsung:

https://api.zena.co.id/v2
  • Autentikasi dengan kunci API dari panel Anda di app.zena.co.id, menu API: Authorization: Bearer b1k_….
  • Endpoint, isi request, dan jawabannya sama dengan dokumentasi di menu API panel tersebut. Ganti alamat dasarnya dengan https://api.zena.co.id/v2.
  • Kunci sandbox (b1k_test_…) menjalankan semua pemeriksaan tanpa benar-benar mengirim pesan.
EndpointFungsi
GET /v2/accountAkun yang dibuka kunci ini
GET /v2/channelsChannel yang terhubung (ID untuk channel_id)
GET /v2/templates?channel_id=…Template WhatsApp yang disetujui
POST /v2/messagesKirim teks, tombol, list, media, lokasi, atau template
GET /v2/messages/{id}Status pesan
GET / PUT / DELETE /v2/webhookAlamat webhook untuk pesan masuk dan status
POST /v2/sandbox/inboundSandbox: membuat pesan masuk tiruan
curl -X POST https://api.zena.co.id/v2/messages \
  -H "Authorization: Bearer b1k_…" \
  -H "Content-Type: application/json" \
  -d '{"channel_id": 12, "to": "6281234567890", "text": "Halo dari API v2"}'

Jika akun Anda juga memakai API v1, jangan mengubah webhook lewat v2. Webhook itu dipakai Zena untuk mengirim DR dan MO v1.