Message Reactions: Send and Receive Emoji Reactions via SMS, MMS, and RCS
Pinnacle is the only messaging API that lets you send and receive emoji reactions on SMS, MMS, and RCS messages. One API call. Any emoji. Every channel.
Ivan

Reactions Shouldn't Be an RCS-Only Feature
Every consumer messaging app has reactions. iMessage, WhatsApp, Telegram โ a thumbs up on a message is the universal "acknowledged." It's how people communicate now. Quick, expressive, zero friction.
But in business messaging? Reactions have been locked behind RCS, leaving the vast majority of SMS and MMS conversations without one of the most natural interaction patterns in modern communication.
Pinnacle changes that. We're the only messaging API that supports emoji reactions across SMS, MMS, and RCS. One endpoint, any emoji, every channel. React to a customer's message with โ to signal resolution. Receive a ๐ from a user confirming their appointment. Track ๐ฅ reactions on a promotional blast as a real-time engagement signal.
It works the way you'd expect โ and on channels where nobody else offers it.
Sending a Reaction
One API call:
import { PinnacleClient } from "rcs-js";
const client = new PinnacleClient({
apiKey: process.env.PINNACLE_API_KEY,
});
await client.messages.react({
messageId: "msg_abc123",
reaction: "๐",
});Pass any Unicode emoji โ ๐ โค๏ธ ๐ ๐ฎ ๐ข โ ๐ฅ ๐ โ and it lands in the conversation thread. Works on SMS, MMS, and RCS messages.
To remove a reaction:
await client.messages.react({
messageId: "msg_abc123",
reaction: null,
});Message Tracking: Precision Reactions
When you react to a message, Pinnacle needs to identify which specific message in the thread to attach the reaction to. This is where message tracking comes in.
When sending a message, enable tracking to get precise reaction targeting:
await client.messages.sms.send({
to: "+14155551234",
from: "+18005550001",
text: "Your order has shipped! Expected delivery: Friday.",
options: {
tracking: "HIDDEN",
},
});Tracking has two modes:
HIDDENโ Invisible identifiers are embedded in the message. Users never see them, but Pinnacle can precisely target the message for reactions. Best for most use cases.IDโ An explicit ID is appended to the message. Visible to the user, but useful for debugging or cases where transparency matters.
If you need to react to a message that was sent without tracking, use the force option:
await client.messages.react({
messageId: "msg_abc123",
reaction: "๐",
options: { force: true },
});The force flag bypasses the tracking requirement โ though without tracking, the reaction may attach to a nearby message in the thread rather than the exact one you intended. For production flows where precision matters, enable HIDDEN tracking on send.
Receiving Reactions From Users
When a user reacts to one of your messages, Pinnacle delivers it via your webhook. Inbound reactions arrive as standard webhook events โ the same endpoint that handles text replies:
app.post("/webhooks/pinnacle", async (req, res) => {
res.status(200).send();
const { event, data } = req.body;
if (event === "MESSAGE.RECEIVED") {
const { from, content } = data;
if (content.reaction) {
console.log(
`${from} reacted with ${content.reaction.emoji}` +
` to message ${content.reaction.messageId}`,
);
await handleReaction(from, content.reaction);
} else if (content.text) {
await handleReply(from, content.text);
}
}
});The content.reaction object gives you:
emojiโ the emoji the user reacted withmessageIdโ the ID of the message they reacted to
No separate webhook setup. No additional configuration. Reactions flow through the same pipeline as every other inbound message.
What You Can Build With Reactions
Confirmation Without Conversation
Replace multi-message confirmation flows with a single reaction prompt:
await client.messages.sms.send({
to: "+14155551234",
from: "+18005550001",
text: "Your appointment is confirmed for tomorrow at 2pm. React ๐ to confirm or reply to reschedule.",
options: { tracking: "HIDDEN" },
});When the ๐ arrives via webhook, log the confirmation and move on โ no back-and-forth needed.
Real-Time Sentiment Signals
Reactions are lightweight sentiment data. A ๐ฅ on a promotional message is engagement. A โค๏ธ on an order confirmation means the experience landed. Track them:
if (content.reaction) {
await analytics.track("message_reaction", {
from: data.from,
emoji: content.reaction.emoji,
messageId: content.reaction.messageId,
timestamp: data.timestamp,
});
}Aggregate reaction data across campaigns and you have a real-time pulse on what resonates โ without surveys, without reply parsing, without NLP.
Support Closure
A customer sends "thanks, that fixed it!" โ react with โ . It's a human signal that the conversation is complete, without forcing the customer to read another message:
await client.messages.react({
messageId: lastCustomerMessageId,
reaction: "โ
",
});Reaction-Based Opt-In
"React โค๏ธ for early access" is a lower bar than "reply YES" โ and more expressive. When the reaction arrives, add them to your list:
if (
content.reaction?.emoji === "โค๏ธ" &&
campaignMessages.has(content.reaction.messageId)
) {
await addToEarlyAccess(data.from);
await client.messages.sms.send({
to: data.from,
from: "+18005550001",
text: "You're in! We'll reach out when early access opens.",
});
}Reactions in the Conversations Dashboard
The Pinnacle conversations dashboard renders reactions inline in the message thread โ the same way consumer messaging apps display them. When a customer reacts to one of your messages, the emoji appears directly beneath the message in the thread as it arrives.
Customer reactions appear inline in the conversations dashboard โ visible to your team in real time.
Frequently Asked Questions
1. Do reactions work on SMS, or just RCS?
Reactions work on SMS, MMS, and RCS โ Pinnacle is the only messaging API that supports reactions across all three channels. This isn't an RCS-exclusive feature.
2. Can I react with any emoji?
Yes. Any standard Unicode emoji works. For best compatibility across devices, stick to commonly supported emoji โ ๐ โค๏ธ ๐ โ ๐ฅ ๐ ๐ฎ.
3. What's the difference between HIDDEN and ID tracking?
HIDDEN embeds invisible identifiers in your message โ users don't see them, but Pinnacle can precisely target the message for reactions. ID appends a visible ID to the message text. HIDDEN is recommended for most production use cases.
4. What happens if I react without tracking enabled?
You can use options.force: true to react without tracking, but the reaction may attach to a nearby message in the thread rather than the exact target. For precision, enable tracking on send.
5. Can I receive reactions from users?
Yes. Inbound reactions arrive via the same MESSAGE.RECEIVED webhook event as text replies. The content.reaction object contains the emoji and the ID of the message that was reacted to.
6. Is there a rate limit on reactions?
Reactions are standard API calls โ the same rate limits that apply to sending messages apply to reactions.
7. Can a message have multiple reactions?
Yes. Messages support multiple reactions.
Key Takeaways
- Cross-channel: Reactions work on SMS, MMS, and RCS โ not just RCS. Pinnacle is the only API that offers this.
- Send reactions:
client.messages.react({ messageId, reaction: "๐" })โ any Unicode emoji. - Remove reactions: Pass
reaction: nullto remove. - Precision tracking: Enable
HIDDENorIDtracking on send for exact reaction targeting. Useforce: trueas a fallback. - Receive reactions: Inbound reactions arrive via
MESSAGE.RECEIVEDwebhook withcontent.reactioncontainingemojiandmessageId. - Dashboard support: Send and view reactions directly from the Pinnacle conversations UI โ no code required.
Get Started
The reactions endpoint is available via the Pinnacle API and all SDKs (TypeScript, Python, Ruby). Enable message tracking for precise reactions, and set up webhooks to receive reactions from users.
Not on Pinnacle yet? Sign up and subscribe to a plan. Questions? Get in touch.
