Send and Remove Message Reactions With UnifyPort
To add a message reaction through UnifyPort, send POST /v1/messages/reaction with the selected account, conversation, message, original sender, and emoji. To remove the connected account’s existing reaction, send the same target with emoji: "". Crucially, sender_id identifies the author of the message being reacted to, not an operator or an arbitrary person whose reaction you want to change.
Key takeaways
- The documented outbound action supports
whatsappandtelegram; receiving reaction events is a separate capability. - Take the target from the selected original message, not from the reaction event’s own message ID.
- Set
sender_idexplicitly for someone else’s message; omission defaults to the connected account. - Treat add and remove as explicit intentions, not a blind toggle retried after every error.
Match the fields to the original message
The React to message reference defines an action on an existing message. It is not a text send containing an emoji, and it does not use a quoted-reply token.
| Request field | Source when selecting a stored message.received event |
|---|---|
account_id | Top-level account_id |
conversation_id | data.conversation.id |
message_id | data.message.id |
sender_id | data.sender.id, the original message author |
emoji | Operator’s chosen emoji, or an empty string for removal |
In a group, the conversation identifies the group while the sender identifies a participant. Do not replace the conversation with the sender just because they look interchangeable in a private chat. Preserve provider identifiers unchanged rather than constructing them from a contact’s display name or telephone number.
There is another important distinction when starting from a message.reaction event: its data.message.id identifies the reaction itself, and data.message.target_message_id points to the original message. Its sender describes the reacting person, not necessarily the original author. Resolve the original stored message before constructing a new action. The reaction webhook tutorial explains that inbound projection; this article covers sending a separate request.
Build an explicit add or remove request
The following JavaScript is an illustrative request builder, not an executed integration test or a complete receiver. Its input is an authenticated, durably stored original event selected by an authorized operator. Function names and error strings are local application code.
function buildReactionRequest(original, emoji) {
if (original.type !== 'message.received' ||
!['whatsapp', 'telegram'].includes(original.provider)) {
throw new Error('Select a supported original message');
}
const conversation = original.data?.conversation;
const message = original.data?.message;
const sender = original.data?.sender;
if (!original.account_id || !conversation?.id ||
!message?.id || !sender?.id) {
throw new Error('Original message context is incomplete');
}
if (typeof emoji !== 'string') {
throw new Error('Choose an emoji or an empty string');
}
return {
account_id: original.account_id,
conversation_id: conversation.id,
message_id: message.id,
sender_id: sender.id,
emoji
};
}
Pass "👍" to request a reaction and "" to remove it. This builder checks context and the value’s type, not whether every possible emoji is accepted upstream. Do not substitute null, omit the removal value, or add an undocumented remove flag.
Send the resulting JSON from your backend to POST /v1/messages/reaction using X-Api-Key. Keep the key out of browser code. Before dispatch, authorize access to the selected messaging account and message, and freeze that selection so newly arriving messages cannot move the target.
If the intended response is a sentence quoting the message rather than a reaction, use the separate WhatsApp quoted-reply workflow. Its opaque reply_token is not a reaction identifier.
Enable the control by action support, not event support
The current provider action matrix and webhook event matrix describe different capabilities:
| Provider | Send a reaction with this endpoint | Reaction event mapping |
|---|---|---|
whatsapp | Supported | Supported |
telegram | Supported | Supported |
line | Not supported | Supported |
twitter, zalo, tiktok, whatsapp-protocol | Not supported | Not supported |
These are documented adapter mappings, not a guarantee of availability for every account or upstream condition. A LINE inbox can understand an incoming reaction without having a working outbound React button. Unsupported combinations return 501 unsupported_by_provider; repeated calls do not add support.
UnifyPort is an unofficial interface for connected accounts. Telegram’s official Bot API separately defines setMessageReaction for bots. Use that native contract when bot identity is the requirement; do not paste its request shape into this endpoint or assume its restrictions define UnifyPort’s behavior.
Keep intention, request outcome, and observation separate
The endpoint documents 200 OK with data.ok: true. Save the actual HTTP outcome and returned request_id; do not convert that result into proof that another person saw the reaction.
Recommended application behavior:
- Serialize actions for the same account and target message. A delayed add must not casually run after a newer remove.
- Record the intended emoji before dispatch and keep the HTTP outcome separately.
- On
react_message_failed, inspect the error and original target. On a timeout, keep the outcome uncertain rather than sending the opposite action as compensation. - If a matching reaction event arrives, update observed state idempotently. Do not assume every request produces a callback or that callbacks arrive in order.
For the observer, configure signing_secret and follow the delivery reference: verify HMAC-SHA256 over the timestamp, a dot, and the raw body, check timestamp freshness, and persist before acknowledgement. Receiving a webhook is not permission to launch a new outbound action automatically.
Acceptance checks and FAQ
Before enabling the control, test an incoming private message, another participant’s group message, a message sent by the account, removal with an empty string, an unsupported provider, and an uncertain network result. Confirm that new arrivals do not change a selected target. These are proposed tests, not reported results.
Does sender_id choose who reacts?
No. It identifies whose message is being reacted to. The connected account performs the action; the field does not let you impersonate another reacting user.
Can I remove a reaction by sending the same emoji again?
The documented removal operation uses emoji: "". Do not implement an assumed toggle.
Why can I receive LINE reactions but not send them?
Inbound event parsing and outbound actions have separate support matrices. The current outbound reaction action maps only to Telegram and WhatsApp.
Next step and sources
Implement the target checks against React to message before adding the inbox control. Keep an unresolved state when evidence is incomplete rather than inventing a reaction lookup or guaranteed replay facility.
References checked on 2026-10-16:
Turn messaging integration into a stable product pipeline.
Start by sending through one API, then bring every inbound message back into your business system with standard events.