Skip to content

Terima Pesan (Webhook) ​

Kirim pesan udah bisa, sekarang giliran nerima. Pasang webhook, terus Zate bakal ngabarin server kamu tiap ada pesan baru, pesan diedit, pesan dihapus, atau pesan dibaca di chat pribadi akun kamu. Cocok buat auto-reply, customer service, atau sekadar nyatet obrolan.

Siapin Endpoint Penerima ​

Bikin endpoint HTTP di server kamu yang nerima POST dengan body JSON. Zate ngirim token kamu di header x-token, jadi cocokin dulu sebelum diproses:

js
// webhook.mjs, pakai Express
import express from 'express';

const app = express();
app.use(express.json());

const handled = new Set(); // Di produksi, simpan di Redis atau database

app.post('/webhook/zate', (req, res) => {
  if (req.header('x-token') !== process.env.ZATE_WEBHOOK_TOKEN) {
    return res.sendStatus(401);
  }

  // Jawab secepatnya: Zate cuma nunggu 10 detik
  res.sendStatus(200);

  const event = req.body;

  // Event yang sama bisa kekirim dua kali, lewatin yang udah pernah diproses
  if (handled.has(event.eventId)) return;
  handled.add(event.eventId);

  if (event.type === 'message' && !event.data.fromMe) {
    console.log(`${event.data.user.name}: ${event.data.text}`);
  }
});

app.listen(3000);

Pasang Webhook-nya ​

Panggil Atur Webhook dengan URL endpoint tadi dan token rahasia buatan kamu sendiri:

bash
curl -X PUT "https://api-zate.azickri.com/session/webhook" \
  -H "id: $ZATE_ID" \
  -H "session-id: $ZATE_SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/webhook/zate","token":"rahasia-banget-123"}'

Mulai detik itu, event baru langsung dikirim ke URL kamu. Pesan sebelum webhook dipasang nggak ikut dikirim. Buat matiin webhook, kirim url dan token kosong.

Bikin token yang susah ditebak

Misalnya hasil openssl rand -hex 16. Token ini satu-satunya cara server kamu tahu request-nya beneran dari Zate, dan dia ikut di setiap request, jadi pakai https kalau URL-nya lewat internet.

Coba Kirim Pesan ​

Minta temen kirim pesan ke akun kamu (atau kirim dari akun lain). Dalam hitungan detik, server kamu nerima event kayak gini:

json
{
  "eventId": "68b3f0a2c1d4e5f6a7b8c9d0:message:1024",
  "type": "message",
  "id": "68b3f0a2c1d4e5f6a7b8c9d0",
  "generatedId": "123456789",
  "data": {
    "chatType": "private",
    "chatId": "111222333",
    "messageId": "1024",
    "fromMe": false,
    "user": {
      "id": "111222333",
      "name": "Budi",
      "username": "budi",
      "phone": "",
      "isBot": false,
      "isContact": true,
      "picture": "https://api-zate.azickri.com/media/68b3f0a2c1d4e5f6a7b8c9d0/111222333/5123456789012345678/profile/c2inRmxRIRLbgqB1x_4xXh.jpg"
    },
    "type": "text",
    "text": "Cak, sate ayam 20 tusuk ya",
    "replyToMessageId": "",
    "created": "2026-10-12T12:00:00.000Z"
  },
  "createdAt": "2026-10-12T12:00:00.120Z"
}

Isi Event ​

Semua event bungkusnya sama:

KolomTipeKeterangan
eventIdstringPengenal event. Event yang sama selalu punya eventId yang sama, jadi pakai ini buat ngelewatin duplikat.
typestringJenis event, lihat tabel di bawah.
idstringid sesi Zate, sama kayak header id. Berguna kalau satu endpoint nerima event dari beberapa sesi.
generatedIdstringID user Telegram dari akun yang login.
dataobjectIsi event, bentuknya tergantung type.
createdAtstringWaktu Zate bikin event ini (ISO 8601).
typeKapan dikirimdata
messageAda pesan masuk, atau akun kamu kirim pesan dari perangkat lainObjek pesan
message.editPesan diedit. Reaksi (emoji) bukan edit, jadi nggak dikirimObjek pesan plus edited
message.deletePesan dihapus{ messageIds }
message.readPesan dibaca{ chatType, chatId, messageId, byMe }

Objek Pesan ​

data buat message dan message.edit:

KolomTipeKeterangan
chatTypestringSelalu private.
chatIdstringID user lawan bicara. Pakai ini sebagai user di Kirim Pesan buat bales.
messageIdstringID pesan.
fromMebooleantrue kalau pesannya dikirim akun kamu sendiri dari perangkat lain (HP, Telegram Desktop).
userobjectLawan bicara, lihat objek user. Selalu orang lain, walaupun fromMe: true.
typestringtext, image, video, audio, document, sticker, location, contact, poll, atau unsupported.
textstringIsi pesan, atau caption buat media.
replyToMessageIdstringID pesan yang dibales. Kosong kalau bukan balasan.
imageobjectKalau type = image: url, thumbnail, mimetype.
videoobjectKalau type = video: url, thumbnail, duration (detik), mimetype.
audioobjectKalau type = audio: url, duration (detik), mimetype, isVoice (true buat voice note).
documentobjectKalau type = document: url, name, size (byte), mimetype.
stickerobjectKalau type = sticker: url, mimetype (image/webp).
locationobjectKalau type = location: latitude, longitude, name, address.
contactobjectKalau type = contact: name, phone.
pollingobjectKalau type = poll: name (pertanyaan) dan options (daftar pilihan).
createdstringWaktu pesan dikirim (ISO 8601).
editedstringCuma di message.edit: waktu pesan diedit.

Semua url dan thumbnail media itu link media publik. Nggak ada yang diunduh sampai link-nya dibuka. Stiker animasi pakai gambar thumbnail-nya biar bisa tampil di tag <img>.

Objek user ​

KolomTipeKeterangan
idstringID user Telegram, sama kayak chatId.
namestringNama tampilan.
usernamestringUsername tanpa @. Kosong kalau nggak punya.
phonestringNomor HP. Kosong kecuali orangnya ngebagiin nomor ke akun kamu.
isBotbooleantrue kalau lawan bicaranya bot.
isContactbooleantrue kalau orangnya ada di kontak kamu.
picturestringLink publik foto profil kecil. Kosong kalau nggak ada fotonya atau disembunyiin privasinya. Foto baru = link baru.

Event Lainnya ​

json
{
  "eventId": "68b3f0a2c1d4e5f6a7b8c9d0:message.edit:1024:1791806460",
  "type": "message.edit",
  "id": "68b3f0a2c1d4e5f6a7b8c9d0",
  "generatedId": "123456789",
  "data": {
    "chatType": "private",
    "chatId": "111222333",
    "messageId": "1024",
    "fromMe": false,
    "user": { "id": "111222333", "name": "Budi", "username": "budi", "phone": "", "isBot": false, "isContact": true, "picture": "" },
    "type": "text",
    "text": "Cak, sate ayam 30 tusuk ya",
    "replyToMessageId": "",
    "created": "2026-10-12T12:00:00.000Z",
    "edited": "2026-10-12T12:01:00.000Z"
  },
  "createdAt": "2026-10-12T12:01:00.090Z"
}
json
{
  "eventId": "68b3f0a2c1d4e5f6a7b8c9d0:message.delete:5821",
  "type": "message.delete",
  "id": "68b3f0a2c1d4e5f6a7b8c9d0",
  "generatedId": "123456789",
  "data": {
    "messageIds": ["1024", "1025"]
  },
  "createdAt": "2026-10-12T12:02:00.050Z"
}
json
{
  "eventId": "68b3f0a2c1d4e5f6a7b8c9d0:message.read:out:111222333:1026",
  "type": "message.read",
  "id": "68b3f0a2c1d4e5f6a7b8c9d0",
  "generatedId": "123456789",
  "data": {
    "chatType": "private",
    "chatId": "111222333",
    "messageId": "1026",
    "byMe": false
  },
  "createdAt": "2026-10-12T12:03:00.070Z"
}
  • message.delete nggak nyebut chat-nya, soalnya Telegram emang nggak ngasih tahu buat chat pribadi. Tenang, ID pesan pribadi itu unik per akun. Penghapusan di grup biasa (basic group) kadang ikut kekirim juga, jadi ID yang nggak kamu kenal boleh dicuekin.
  • message.read artinya semua pesan sampai messageId udah dibaca. byMe: true berarti akun kamu yang baca chat orang itu, byMe: false berarti orang itu yang baca pesan dari kamu.

Bales Otomatis ​

chatId bisa langsung dipakai sebagai user buat bales:

js
async function balas(chatId, text) {
  await fetch('https://api-zate.azickri.com/message', {
    method: 'POST',
    headers: {
      'id': process.env.ZATE_ID,
      'session-id': process.env.ZATE_SESSION_ID,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ user: chatId, type: 'text', text }),
  });
}

// Di dalam handler webhook
if (event.type === 'message' && !event.data.fromMe && !event.data.user.isBot) {
  await balas(event.data.chatId, 'Siap, pesanan lagi dibakar! 🔥');
}

Jangan bales pesan sendiri

Selalu cek fromMe. Pesan yang kamu kirim dari HP juga masuk sebagai event message dengan fromMe: true, dan ngebales itu bisa bikin obrolan muter-muter sendiri. Pesan yang dikirim lewat Kirim Pesan sendiri nggak dikirim ke webhook, soalnya response-nya udah ngasih messageId.

Pengiriman dan Retry ​

  • Event dikirim satu per satu per akun, sesuai urutan Zate nerimanya. Kalau urutan penting banget, urutin pakai created.
  • Response 2xx dianggap berhasil. Zate nunggu jawaban maksimal 10 detik.
  • Error jaringan, timeout, 429, dan 5xx dicoba ulang setelah 2 detik, 10 detik, lalu 30 detik. Kalau masih gagal, event-nya dibuang.
  • 4xx lainnya (misalnya 401 karena token salah) nggak dicoba ulang, soalnya ngirim ulang juga nggak bakal ngubah apa-apa.
  • Maksimal 1.000 event ngantre per akun. Kalau lebih, yang paling lama dibuang duluan.
  • Retry atau susulan pesan bisa ngirim event yang sama dua kali. Lewatin eventId yang udah pernah kamu proses.

Pesan yang Kelewat ​

Zate nyimpen posisi update terakhir tiap akun. Tiap kali nyambung lagi, entah habis server restart atau koneksi sempat putus, Zate nanya ke Telegram apa aja yang kelewat dan nyusulin event-nya ke webhook kamu.

  • Webhook yang baru dinyalain mulai dari detik itu. Histori sebelumnya nggak dikirim.
  • Reset Session atau login ulang juga mulai dari awal lagi.
  • Habis downtime yang lama, pesan susulan bisa udah berjam-jam umurnya. Kalau kamu bikin auto-reply, cek created dulu biar nggak bales pesan basi.

Yang Nggak Dikirim ​

  • Pesan di grup dan channel. Webhook cuma buat chat pribadi.
  • Saved Messages, alias chat akun kamu sama dirinya sendiri.
  • Pesan layanan, misalnya notifikasi screenshot atau "bergabung ke Telegram".
  • Reaksi emoji.
  • Pesan yang dikirim lewat Kirim Pesan.