Skip to content

Zalo Webhook Events

This page documents the webhook event structure that Zalo sends to your bot's webhook URL.

Overview

When a user interacts with your bot, Zalo sends an HTTP POST request to your webhook URL with a JSON payload.

Request Details

PropertyValue
URLYour configured webhook URL
MethodPOST
Content-Typeapplication/json
HeadersX-Bot-Api-Secret-Token: <your-secret-token>

⚠️ Always verify the X-Bot-Api-Secret-Token header before processing the request to ensure it's from Zalo.

Payload Structure

json
{
  "ok": true,
  "result": {
    "event_name": "message.text.received",
    "message": {
      "from": { ... },
      "chat": { ... },
      "text": "...",
      "message_id": "...",
      "date": 1750316131602
    }
  }
}

Event Types

event_nameDescription
message.text.receivedText message received
message.image.receivedImage message received
message.sticker.receivedSticker message received
message.voice.receivedVoice message received
message.unsupported.receivedUnsupported message type (special user groups)

Message Fields

from (object)

Information about the sender:

FieldTypeDescription
idstringUser ID
display_namestringUser's display name
is_botbooleanWhether the sender is a bot

chat (object)

Information about the conversation:

FieldTypeDescription
idstringChat ID
chat_typestringPRIVATE or GROUP

Message content fields

FieldTypeDescription
textstringText content (for text messages)
photostringImage URL (for image messages)
captionstringCaption text (for image messages)
stickerstringSticker ID
voice_urlstringAudio file URL (for voice messages, .aac format)
message_idstringUnique message ID
datenumberTimestamp in milliseconds

Message Type Examples

Text message

json
{
  "ok": true,
  "result": {
    "event_name": "message.text.received",
    "message": {
      "from": { "id": "user-id", "display_name": "Ted", "is_bot": false },
      "chat": { "id": "chat-id", "chat_type": "PRIVATE" },
      "text": "Xin chào",
      "message_id": "msg-id",
      "date": 1750316131602
    }
  }
}

Image message

json
{
  "ok": true,
  "result": {
    "event_name": "message.image.received",
    "message": {
      "from": { "id": "user-id", "display_name": "Ted", "is_bot": false },
      "chat": { "id": "chat-id", "chat_type": "PRIVATE" },
      "photo": "https://example.com/image.jpg",
      "caption": "Photo caption",
      "message_id": "msg-id",
      "date": 1750316131602
    }
  }
}

Voice message

json
{
  "ok": true,
  "result": {
    "event_name": "message.voice.received",
    "message": {
      "from": { "id": "user-id", "display_name": "Ted", "is_bot": false },
      "chat": { "id": "chat-id", "chat_type": "PRIVATE" },
      "voice_url": "https://example.com/audio.aac",
      "message_id": "msg-id",
      "date": 1750316131602
    }
  }
}

Special Notes

  • Unsupported messages: For certain user groups (children, disabled, illiterate), the system sends message.unsupported.received instead of the actual message content to comply with legal regulations.
  • Voice messages: Only .aac format is supported. Voice messages can only be sent in 1-on-1 chats, not groups.
  • Rate limiting: The testWebhook() API is rate-limited per bot per day.

Debugging

If you're not receiving events:

  1. Call bot.testWebhook() to diagnose connectivity issues
  2. Check that your webhook URL is publicly accessible (not localhost or internal IP)
  3. Verify the X-Bot-Api-Secret-Token header matches your configured secret
  4. Ensure your server responds with 200 OK quickly

Next

Last updated: September 11, 2026

Built for Vietnamese developers, documented for everyone.