Skip to content
AISARAISAR
Messages

Messages

Message created

message.created

Fires when a new message is created — inbound from a contact or outbound from a user or the system. For ad-originated messages (WhatsApp Click-to-Message and Instagram click-to-Direct ads) the payload includes a referral object with ad details. WhatsApp quick-reply and interactive button taps are normalized into a regular text message: the button label goes to content.text and the button payload to content.interactive_reply_payload.

HTTP request

AISAR sends a signed POST request to your webhook_url:

http
POST {webhook_url}
Content-Type: application/json
X-AISAR-Signature: sha256=...

{
  "event": "message.created",
  "event_id": "evt_01JXXXXXXXXXXXXXXXXXXXXX",
  "timestamp": "2026-03-22T12:00:00.000000Z",
  "company_id": 1,
  "data": {
    "message": {
      "id": 123,
      "external_id": "3EB0ABC123456789",
      "conversation_id": 45,
      "thread_id": 67,
      "channel_id": 2,
      "direction": "inbound",
      "type": "text",
      "content": {
        "text": "Hello, I need help"
      },
      "display_content": "Hello, I need help",
      "media": null,
      "reply_to_message_id": null,
      "is_deleted": false,
      "system_notification": false,
      "created_at": "2026-03-22T12:00:00.000000Z"
    },
    "sender": {
      "type": "contact",
      "contact_id": 10,
      "contact_account_id": 15,
      "name": "John Doe",
      "phone": "+77001234567",
      "email": "john@example.com",
      "external_id": "77001234567@s.whatsapp.net"
    },
    "recipient": null,
    "conversation": {
      "id": 45,
      "status": "active",
      "contact_id": 10,
      "deal_id": null,
      "subject": null,
      "is_archived": false,
      "is_group": false
    },
    "channel": {
      "id": 2,
      "type": "whatsapp",
      "name": "Main WhatsApp"
    },
    "is_dialog_assigned": false,
    "referral": null
  }
}

The X-AISAR-Signature header carries an HMAC-SHA256 signature of the request body — use it to verify the request came from AISAR.

Example payload

json
{
  "event": "message.created",
  "event_id": "evt_01JXXXXXXXXXXXXXXXXXXXXX",
  "timestamp": "2026-03-22T12:00:00.000000Z",
  "company_id": 1,
  "data": {
    "message": {
      "id": 123,
      "external_id": "3EB0ABC123456789",
      "conversation_id": 45,
      "thread_id": 67,
      "channel_id": 2,
      "direction": "inbound",
      "type": "text",
      "content": {
        "text": "Hello, I need help"
      },
      "display_content": "Hello, I need help",
      "media": null,
      "reply_to_message_id": null,
      "is_deleted": false,
      "system_notification": false,
      "created_at": "2026-03-22T12:00:00.000000Z"
    },
    "sender": {
      "type": "contact",
      "contact_id": 10,
      "contact_account_id": 15,
      "name": "John Doe",
      "phone": "+77001234567",
      "email": "john@example.com",
      "external_id": "77001234567@s.whatsapp.net"
    },
    "recipient": null,
    "conversation": {
      "id": 45,
      "status": "active",
      "contact_id": 10,
      "deal_id": null,
      "subject": null,
      "is_archived": false,
      "is_group": false
    },
    "channel": {
      "id": 2,
      "type": "whatsapp",
      "name": "Main WhatsApp"
    },
    "is_dialog_assigned": false,
    "referral": null
  }
}

Payload fields

FieldTypeDescription
message.idintegerInternal message ID
message.external_idstringExternal message ID (platform-specific, e.g. WhatsApp message ID)
message.conversation_idintegerConversation ID
message.thread_idintegerThread ID
message.channel_idintegerChannel ID
message.directionstringinbound (from contact) or outbound (from user/system)
message.typestringMessage type: text, image, video, audio, voice, document, sticker, location, contact, poll, template, ig_post, ig_reel, ig_story, ig_story_reply, story_mention, share, ephemeral, unsupported_type, comment, call, note. voice is a voice note (recorded audio, OGG/Opus); plain audio is an audio file. template, ig_post, ig_reel, ig_story, ig_story_reply, story_mention, share, ephemeral, unsupported_type are Instagram attachment kinds (a generic-template card, a reposted post/reel/story, a story reply, a story mention, a shared post, a self-destructing attachment, and an unrecognized attachment respectively): for these, message.type inherits the type of the message's first attachment (see message.media[].type below) rather than being set independently. comment is a comment on an Instagram/Threads post (a comment thread). call is a call card in the timeline. note is an internal operator note (never leaves AISAR). WhatsApp quick-reply / interactive button taps are normalized to text. Reactions are NOT a message_type — they are delivered via the separate `message.reaction.updated` event, not through `message.created`
message.contentobject|stringMessage content. For text: {"text": "..."}. For media it includes caption, mime_type, etc. For button taps it also carries interactive_reply_payload — the button payload / list option id (null when the template button has no payload)
message.display_contentstring|nullPlain text representation of the message content
message.mediaarray|nullAttachments array; null if the message has no media. Each item carries only type/url/filename/mime_type/size/duration/playback_url — an attachment's metadata field (download_status, media_id, IG card contents, etc.) is NOT included in the webhook payload; it is available only via REST (GET /messages/{id} or .../attachments)
message.media[].typestringAttachment type (the full message_attachments.type CHECK-constraint set): image, video, audio, voice, document, file, contact, location, sticker, ig_reel, ig_post, ig_story, share, ig_story_reply, story_mention, ephemeral, unsupported_type, template
message.media[].urlstringDownload URL
message.media[].filenamestring|nullOriginal filename
message.media[].mime_typestring|nullMIME type (e.g. image/jpeg; voice notes are audio/ogg)
message.media[].sizeinteger|nullFile size in bytes
message.media[].durationinteger|nullDuration in seconds (audio / voice / video)
message.media[].playback_urlstring|nullA signed link to the m4a/AAC version of a voice attachment (lazily transcoded from the original OGG/Opus). Present only for voice attachments and audio/ogg attachments whose file is already stored locally; null otherwise. In the webhook the link is temporary — valid for 7 days (the REST version of the same field, MessageAttachment.playback_url, never expires)
message.reply_to_message_idinteger|nullID of the quoted message, if this is a reply
message.is_deletedbooleanWhether the message has been deleted
message.system_notificationbooleanWhether this is a system notification
message.created_atstringISO 8601 timestamp
sender.typestringcontact for inbound messages, user for outbound
sender.contact_idintegerContact ID (inbound messages)
sender.contact_account_idintegerContact account ID (inbound messages)
sender.user_idintegerUser ID (outbound messages)
sender.namestringSender display name
sender.phonestring|nullContact phone number (inbound messages)
sender.emailstring|nullSender email
sender.external_idstringPlatform-specific contact ID, e.g. 77001234567@s.whatsapp.net (inbound messages)
recipientobject|nullPresent only for outbound messages: the recipient contact, same structure as sender with type=contact
conversation.idintegerConversation ID
conversation.statusstringactive or closed
conversation.contact_idintegerContact ID
conversation.deal_idinteger|nullAssociated deal ID
conversation.subjectstring|nullConversation subject
conversation.is_archivedbooleanWhether the conversation is archived
conversation.is_groupbooleantrue if this conversation is a group chat (e.g. a WhatsApp group), false for direct messages
channel.idintegerChannel ID
channel.typestringChannel type key: `whatsapp`, `whatsapp_business`, `instagram`, `instagram_business`, `threads`, `telegram`, `telegram_bot`, `live_chat`
channel.namestringChannel name
is_dialog_assignedbooleantrue if a user is assigned to this conversation, false otherwise
referralobject|nullReferral data for ad-originated messages; null for regular messages. Populated on the first message of a conversation started from an ad, for the `whatsapp`, `whatsapp_business` (Click-to-WhatsApp) and `instagram_business` (click-to-Direct ads) channels. The field set depends on the channel: WhatsApp supplies `source_url`, `ctwa_clid`, `body`, `media_type`; Instagram supplies `post_id` and `ref`. Common to both: `source_type`, `source_id`, `headline`, `image_url`, `video_url`. Always null for other channel types.
referral.source_urlstringURL of the ad or post; WhatsApp only
referral.source_typestringSource type: ad or post
referral.source_idstringAd or post ID (`ad_id` for Instagram); can be used with the Meta API to get campaign details
referral.headlinestringAd headline text
referral.bodystringAd body text
referral.ctwa_clidstringClick tracking ID for precise attribution; WhatsApp only
referral.media_typestringMedia type in the ad: image or video; WhatsApp only
referral.image_urlstringURL of the ad image (if applicable)
referral.video_urlstringURL of the ad video (if applicable)
referral.post_idstringID of the post the ad ran from; Instagram only
referral.refstringArbitrary label from the deep link (`ig.me/...?ref=`) when set; Instagram only