---
title: "Send reaction to message"
method: POST
path: "/v1/messages/{messageId}/reactions"
---

# Send reaction to message

`POST /v1/messages/{messageId}/reactions`

## Request

- Base URL: `https://api.zavu.dev`
- URL: `https://api.zavu.dev/v1/messages/{messageId}/reactions`
- Auth: HTTP bearer

Send an emoji reaction to an existing WhatsApp message. Reactions are only supported for WhatsApp messages.

## Path parameters

- `messageId` string, required

## Headers

- `Zavu-Sender` string

## Request body

- ReactionRequest
  - `emoji` string, required: Single emoji character to react with.

## Response `202`

Reaction accepted for delivery.

- MessageResponse
  - `message` Message, required
    - `id` string, required
    - `to` string, required
    - `from` string
    - `senderId` string
    - `channel` 'auto' | 'sms' | 'sms_oneway' | 'whatsapp' | 'telegram' | 'email' | 'instagram' | 'messenger' | 'voice', required: Delivery channel. Use 'auto' for intelligent routing.
    - `direction` 'inbound' | 'outbound', required: Who sent the message. Needed to render a thread: `status` cannot tell the two apart, because an inbound message is also stored as `delivered`.
    - `messageType` 'text' | 'image' | 'video' | 'audio' | 'document' | 'sticker' | 'location' | 'contact' | 'buttons' | 'list' | 'cta_url' | 'request_contact_info' | 'location_request' | 'reaction' | 'template', required: Type of message. Non-text types are supported by WhatsApp and Telegram (varies by type). `location_request` asks the recipient to share their location and is WhatsApp-only. It takes no `content` object — the prompt goes in `text` (max 1024 characters) and the button label is fixed by WhatsApp. The recipient's answer arrives as an inbound `location` message whose `content.replyToMessageId` is the ID of the request. `request_contact_info` asks the recipient to share their phone number and is WhatsApp-only. Like `location_request` it takes no `content` object — the prompt goes in `text` (max 1024 characters) and WhatsApp renders a fixed **Share Contact Info** button. The answer arrives as an inbound `contact` message. Use it to recover the phone number of a contact who adopted a WhatsApp username and is only known by their business-scoped user ID (BSUID); when they share it, Zavu automatically links the phone number to that contact.
    - `status` 'queued' | 'sending' | 'sent' | 'delivered' | 'read' | 'failed' | 'received' | 'pending_url_verification', required
    - `text` string: Text content or caption.
    - `content` MessageContent: Content for non-text message types (WhatsApp and Telegram).
      - `mediaUrl` string: URL of the media file (for image, video, audio, document, sticker).
      - `mediaId` string: WhatsApp media ID if already uploaded.
      - `mimeType` string: MIME type of the media.
      - `filename` string: Filename for documents.
      - `latitude` number: Latitude for location messages.
      - `longitude` number: Longitude for location messages.
      - `locationName` string: Name of the location.
      - `locationAddress` string: Address of the location.
      - `contacts` object[]: Contact cards for contact messages.
        - `name` string
        - `phones` string[]
      - `buttons` object[]: Interactive buttons (max 3).
        - `id` string, required
        - `title` string, required
      - `listButton` string: Button text for list messages.
      - `sections` object[]: Sections for list messages.
        - `title` string, required
        - `rows` object[], required
          - `id` string, required
          - `title` string, required
          - `description` string
      - `ctaDisplayText` string: Button label for cta_url messages.
      - `ctaUrl` string, uri: Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url. WhatsApp requires HTTPS in production.
      - `ctaHeaderType` 'text' | 'image' | 'video' | 'document': Optional header type for cta_url messages.
      - `ctaHeaderText` string: Header text when ctaHeaderType is 'text'.
      - `ctaHeaderMediaUrl` string, uri: Public HTTPS URL of the header media when ctaHeaderType is 'image', 'video', or 'document'. WhatsApp fetches this URL — it must be publicly reachable and return the declared content type.
      - `footerText` string: Optional footer text for cta_url messages.
      - `emoji` string: Emoji for reaction messages.
      - `reactToMessageId` string: Message ID to react to.
      - `replyToMessageId` string: Zavu message ID of the quoted message this message replies to. **When sending:** set it to make the message a quoted reply to an earlier message in the same conversation, such as the `data.messageId` of a `message.inbound` webhook. Supported on WhatsApp (every message type except `reaction`) and Telegram. The quoted message must belong to this project (otherwise `404`), must have gone out on the same channel as this send, must belong to the conversation `to` addresses, must have been sent or received by the sender this message goes out from (pass it in `Zavu-Sender`), and must have a `providerMessageId`. A failed message and a Telegram button tap cannot be quoted. Any other case returns `400` instead of sending the message unquoted. The stored message then carries the rest of the `replyTo*` fields. **When receiving:** present on inbound messages that quote an earlier message. Omitted when the quoted message is not found in Zavu (e.g. an old or unknown message) — use replyToProviderMessageId in that case.
      - `replyToProviderMessageId` string: Provider message ID (WhatsApp WAMID, or Telegram message ID) of the quoted message. Present whenever an inbound WhatsApp message is a reply, even if the quoted message is not stored in Zavu, and on outbound quoted replies. Read-only: to quote when sending, set replyToMessageId.
      - `replyToFrom` string: Sender of the quoted message (phone number in E.164 format, or the Telegram chat ID). Read-only.
      - `replyToText` string: Truncated snippet (up to 200 characters) of the quoted message's text, for display. Empty when the quoted message has no text (e.g. media). Read-only.
      - `replyToMessageType` string: Type of the quoted message (text, image, video, etc.). Read-only.
      - `templateId` string: Template ID for template messages.
      - `templateVariables` object: Variables for body placeholders. Key them to match the template body: by position (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for named templates. Zavu detects the template's format and sends the correct payload to Meta. Named keys also resolve a named text-header variable. Do not mix positional and named keys in the same request. On WhatsApp, a value may not contain line breaks, tabs or more than 4 consecutive spaces: Meta refuses those at delivery, so the send is rejected with `400 invalid_request` instead (`details.field` names the value).
      - `templateButtonVariables` object: Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array — not the placeholder name. Values substitute the `{{1}}` placeholder inside that button's URL. **WhatsApp constraints:** - URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no name. Named placeholders like `{{token}}` are stored as literal URL text by Meta and cannot be substituted. - At most one placeholder per URL button. - A template may have at most three buttons. - Static URL buttons (no placeholder) and `quick_reply` buttons are not included here.
      - `templateHeaderVariables` object: Value for a text-header variable, keyed by `1` (WhatsApp text headers allow at most one variable). Optional override. If omitted, Zavu resolves the header from `templateVariables` using the header placeholder's name (e.g. `novios`). Static text headers need no value. The same WhatsApp rule as `templateVariables` applies: no line breaks, tabs or more than 4 consecutive spaces.
      - `referral` AdReferral: Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from. WhatsApp only. Present on the **first inbound message** of a conversation opened from a Meta ad or post, and on no message after it — so store it when it arrives rather than expecting it again. Organic conversations never carry it. Field names are camelCased to match the rest of this API; Meta sends them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
        - `sourceType` 'ad' | 'post': Where the click came from.
        - `sourceId` string: Identifier of the ad or post that produced the click.
        - `sourceUrl` string, uri: Meta permalink to the ad or post.
        - `headline` string: Headline of the ad or post.
        - `body` string: Body copy of the ad or post.
        - `mediaType` 'image' | 'video': Type of media on the ad, when it had any.
        - `imageUrl` string, uri: Image of the ad. Present when `mediaType` is `image`.
        - `videoUrl` string, uri: Video of the ad. Present when `mediaType` is `video`.
        - `thumbnailUrl` string, uri: Thumbnail of the ad media.
        - `ctwaClid` string: Click-to-WhatsApp click identifier. This is the value Meta's Conversions API needs to credit a conversion back to the ad that produced the conversation. Present on `ad` sources; a `post` source has none.
    - `conversationId` string: ID of the conversation (inbox thread) this message belongs to. Use it to build a direct dashboard link: `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`. Omitted only on legacy messages created before conversation threading.
    - `providerMessageId` string: Message ID from the delivery provider.
    - `errorCode` string, nullable
    - `errorMessage` string, nullable
    - `cost` number, nullable: Zavu platform charge in USD for this message. Messaging is billed against your plan's monthly limits plus usage-based overage.
    - `costProvider` number, nullable: Carrier and delivery cost in USD.
    - `costTotal` number, nullable: Total cost in USD (platform charge + delivery cost).
    - `metadata` object
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time

## Other responses

- `400`: Invalid request or message is not from WhatsApp.
- `401`: Unauthorized.
- `404`: Message not found.

## Changes

> 80 revisions in range; 1 not diffed.

- **2026-08-20** `3bb0b5a6239a`: 1 info
  - added the required property `message/direction` to the response with the `202` status
- **2026-08-14** `2c1380fb5232`: 1 info
  - added the optional property `message/content/referral` to the response with the `202` status
- **2026-08-01** `23fe6e10876d`: 1 warning
  - added the new `request_contact_info` enum value to the `message/messageType` response property for the response status `202`
- **2026-07-27** `6c569eed4849`: 1 warning
  - added the new `location_request` enum value to the `message/messageType` response property for the response status `202`
- **2026-07-23** `12d6d706121e`: 1 info
  - removed the `whatsapp_alt` enum value from the `message/channel` response property for the response status `202`

[Operation history](https://skmtc.dev/zavudev/apis/zavu-unified-messaging-layer-api/changes/v1/messages/:messageId/reactions/post.md)

---

[API](https://skmtc.dev/zavudev/apis/zavu-unified-messaging-layer-api.md) - [All operations](https://skmtc.dev/zavudev/apis/zavu-unified-messaging-layer-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zavudev/apis/zavu-unified-messaging-layer-api/revisions/75bc777b0ffb?raw)
