sgeraDocs
API reference

Webhook events

Reference of the message, status, account and campaign variation payloads Msgera POSTs to your webhook URL.

Msgera POSTs a JSON payload to your configured webhook URL for every event your webhook is subscribed to. Message events are dispatched to either your personal or group webhook URL depending on whether the chat is a 1:1 chat or a group chat. Account status and campaign variation events always go to your personal URL. See Webhooks for how to configure URLs, subscriptions and filters, verify signatures, and handle retries.

Event types

Each webhook chooses which events it receives. Webhooks that existed before event subscriptions were introduced receive message.inbound only until you subscribe them to more.

FieldTypeDescription
message.inboundpersonal | groupA customer sent a message to one of your devices.
message.statuspersonal | groupA message you sent moved to SENT, DELIVERED, READ or FAILED.
message.editedpersonal | groupA message in the chat was edited.
message.deletedpersonal | groupA message in the chat was deleted for everyone.
message.reactionpersonal | groupAn emoji reaction was added, changed or removed.
account.statuspersonalA device connected, disconnected, needs a QR scan, or was restricted by WhatsApp.

Every request also names its event in the X-Msgera-Event header, so you can route a delivery before parsing the body.

Envelope

All event types except message.inbound share one envelope. The event-specific fields live under data.

{
  "event": "message.status",
  "id": "ack_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED_delivered",
  "occurredAt": "2026-04-12T10:11:25.000Z",
  "data": { ... }
}
FieldTypeDescription
eventstringEvent type, for example message.status.
idstringIdempotency key of the event. The same underlying event always has the same id, across retries and redeliveries. Equal to the X-Msgera-Idempotency-Key header.
occurredAtstringISO 8601 time the event happened.
dataobjectEvent-specific fields, described below.

message.inbound

Inbound messages keep their original flat payload, so existing integrations continue to work. The event, id and occurredAt fields are added alongside the message fields rather than wrapping them in data. Fields that do not apply to a message are omitted.

{
  "accountId": "acc_7d1c9e",
  "deviceUid": "dev_4f8c2a1e",
  "from": "15551234567",
  "fromName": "Zeyad",
  "text": "When will my order arrive?",
  "timestamp": "2026-04-12T10:11:23.000Z",
  "providerMessageId": "618D49720368605B82BBC72AA9CAE4ED",
  "isGroup": false,
  "mediaId": null,
  "event": "message.inbound",
  "id": "msg_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED",
  "occurredAt": "2026-04-12T10:11:23.000Z"
}
FieldTypeDescription
eventstringAlways message.inbound.
idstringIdempotency key: stable for this message across retries and redeliveries. Use it to dedupe.
occurredAtstringSame value as timestamp.
accountIdstringDevice the message arrived on.
deviceUidstring | nullUID of that device, the same value you pass as deviceUid when sending.
fromstringSender phone number.
fromNamestringSender display name if WhatsApp exposed one.
textstringMessage text or media caption. Omitted for media without a caption.
mediaTypestringimage | video | audio | document | sticker. Omitted for text messages.
mediaMimeTypestringMIME type of the attachment, for example image/jpeg.
mediaFileNamestringOriginal file name, when WhatsApp provides one (usually documents).
mediaSizenumberAttachment size in bytes.
isVoiceNotebooleanAudio only: true when the audio was recorded as a voice note.
mediaIdstring | nullId of the stored attachment in your Msgera media library, or null for text messages and attachments that could not be stored.
timestampstringISO 8601 timestamp the provider stamped on the message.
providerMessageIdstringUnderlying WhatsApp message id. Edit, delete, reaction and status events refer to messages by this id.
isGroupbooleanTrue when the message came from a group chat.
groupIdstringProvider id of the group chat (only when isGroup is true).
groupNamestringDisplay name of the chat, when known.

Inbound media

Webhook payloads never contain the media bytes. Msgera stores each attachment of up to 16 MB in your media library and sends its mediaId together with mediaMimeType, mediaFileName and mediaSize. Look the asset up with the Media endpoints using mediaId. When an attachment could not be stored, mediaType is still set and mediaId is null.

What is not delivered

Typing indicators, status (Stories) updates and empty system messages are filtered out server-side. Edits, deletes and reactions are not delivered as inbound messages - they arrive as their own events below.

message.status

Sent when a message you sent from a device advances to a new delivery status. Statuses only move forward: once a message is READ you will not receive DELIVERED or SENT for it again, and a status that arrives late or repeats is not delivered. Each status of a message is sent at most once and has its own id.

{
  "event": "message.status",
  "id": "ack_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED_delivered",
  "occurredAt": "2026-04-12T10:11:25.000Z",
  "data": {
    "messageId": "5c2e9a7b-1d3f-4e8a-b6c4-9f0d2e1a3b5c",
    "providerMessageId": "618D49720368605B82BBC72AA9CAE4ED",
    "accountId": "acc_7d1c9e",
    "status": "DELIVERED",
    "to": "15551234567",
    "isGroup": false,
    "groupId": null,
    "campaignId": null,
    "error": null
  }
}
FieldTypeDescription
messageIdstringMsgera message id.
providerMessageIdstringUnderlying WhatsApp message id.
accountIdstringDevice the message was sent from.
statusstringSENT | DELIVERED | READ | FAILED.
tostringRecipient of the message.
isGroupbooleanTrue when the message was sent in a group chat.
groupIdstring | nullGroup id for group messages, otherwise null.
campaignIdstring | nullCampaign the message belongs to, or null for direct sends.
errorstring | nullFailure reason when status is FAILED, otherwise null.

message.edited

Sent when a message in the chat is edited. In a group, only edits by the original author are accepted.

{
  "event": "message.edited",
  "id": "edit_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED_1775988720000",
  "occurredAt": "2026-04-12T10:12:00.000Z",
  "data": {
    "messageId": "5c2e9a7b-1d3f-4e8a-b6c4-9f0d2e1a3b5c",
    "providerMessageId": "618D49720368605B82BBC72AA9CAE4ED",
    "accountId": "acc_7d1c9e",
    "from": "15551234567",
    "isGroup": false,
    "groupId": null,
    "text": "When will my order #1042 arrive?",
    "previousText": "When will my order arrive?"
  }
}

message.deleted

Sent once when a message is deleted for everyone. In a group, only deletes by the original author are accepted.

{
  "event": "message.deleted",
  "id": "del_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED",
  "occurredAt": "2026-04-12T10:13:30.000Z",
  "data": {
    "messageId": "5c2e9a7b-1d3f-4e8a-b6c4-9f0d2e1a3b5c",
    "providerMessageId": "618D49720368605B82BBC72AA9CAE4ED",
    "accountId": "acc_7d1c9e",
    "from": "15551234567",
    "isGroup": false,
    "groupId": null
  }
}

message.reaction

Sent when someone reacts to a message, changes their reaction, or removes it. A removed reaction has emoji set to null and removed set to true. Each person has at most one reaction per message; a newer reaction replaces the previous one.

{
  "event": "message.reaction",
  "id": "react_acc_7d1c9e_618D49720368605B82BBC72AA9CAE4ED_15551234567_1775988780000",
  "occurredAt": "2026-04-12T10:13:00.000Z",
  "data": {
    "messageId": "5c2e9a7b-1d3f-4e8a-b6c4-9f0d2e1a3b5c",
    "providerMessageId": "618D49720368605B82BBC72AA9CAE4ED",
    "accountId": "acc_7d1c9e",
    "from": "15551234567",
    "isGroup": false,
    "groupId": null,
    "emoji": "👍",
    "removed": false
  }
}
Fields of message.edited, message.deleted and message.reaction
FieldTypeDescription
messageIdstringMsgera id of the message that changed. It can be a message you received or one you sent.
providerMessageIdstringWhatsApp id of the message that changed.
accountIdstringDevice the chat belongs to.
fromstringPhone number of the person who edited, deleted or reacted.
isGroupbooleanTrue when the message is in a group chat.
groupIdstring | nullGroup id for group chats, otherwise null.
textstringmessage.edited only: the new text.
previousTextstring | nullmessage.edited only: the text before this edit.
emojistring | nullmessage.reaction only: the reaction emoji, or null when the reaction was removed.
removedbooleanmessage.reaction only: true when the reaction was removed.

account.status

Sent to your personal URL when a device's status changes. Filters do not apply to this event.

{
  "event": "account.status",
  "id": "acct_acc_7d1c9e_disconnected_42",
  "occurredAt": "2026-04-12T11:02:17.000Z",
  "data": {
    "accountId": "acc_7d1c9e",
    "status": "DISCONNECTED",
    "phone": "15550001111",
    "reasonCode": "SESSION_DISCONNECTED",
    "previousStatus": "CONNECTED"
  }
}
FieldTypeDescription
accountIdstringDevice whose status changed.
statusstringCONNECTED | CONNECTING | DISCONNECTED | QR_REQUIRED | BLOCKED for connection changes; RESTRICTED | UNRESTRICTED when WhatsApp temporarily restricts the device or lifts the restriction.
phonestring | nullPhone number of the device, when known.
reasonCodestring | nullMachine-readable reason, when available.
previousStatusstringConnection changes only: the status before this change.
untilstring | nullRESTRICTED only: when WhatsApp lifts the restriction, if known.
reasonstringRESTRICTED and UNRESTRICTED only: why the device was restricted.

Restricted devices pause campaigns

While a device is RESTRICTED, Msgera refuses new sends on it and pauses its running campaigns. They resume automatically when the restriction is lifted and an UNRESTRICTED event is sent.

Campaign variation events

When a linked-device TEXT campaign needs more wording variety than its text provides, it waits in AWAITING_REVIEW while message variations are generated. Msgera notifies the personalwebhook URL of the campaign's primary device when generation finishes, so you can review and approve them with the Campaign Variations endpoints instead of polling. These events are delivered to every active webhook with a personal URL, regardless of its event subscriptions and filters, and are signed like every other delivery - see Verify the signature. They use their own flat payload; tell them apart by the event field.

{
  "event": "campaign.variations_ready",
  "campaignId": "uuid",
  "campaignName": "Summer Sale",
  "variationStatus": "PENDING_REVIEW",
  "required": 19,
  "capacity": 24,
  "timestamp": "2026-05-22T10:02:41.000Z"
}
FieldTypeDescription
eventstringcampaign.variations_ready or campaign.variations_failed.
campaignIdstringId of the campaign awaiting review.
campaignNamestringCampaign name.
variationStatusstringPENDING_REVIEW when ready, FAILED when generation failed.
requirednumberUnique messages the audience needs: ceil(recipients / 8).
capacitynumberUnique messages the approved options currently produce (variations_ready only).
errorstringWhy generation failed (variations_failed only). Retry with the regenerate endpoint or delete the campaign.
timestampstringISO 8601 time the event was emitted.

Auto-approved campaigns

A campaign created with autoApproveVariations: true starts sending as soon as the generated options cover required, and no campaign.variations_ready event is sent. If they fall short, the campaign stays in review and the event is delivered as usual. Generation that fails, including when the monthly AI template generation quota is exhausted, always sends campaign.variations_failed.