WhatsApp Quoted Replies: reply_token vs Parent Message ID
To quote a WhatsApp message through UnifyPort, copy that message’s data.message.reply_token unchanged into reply_to.reply_token in a separate send request. Do not substitute data.message.reply_to_message_id: that field identifies the parent of an incoming quoted reply, not the message you just received. If the token is missing, do not manufacture one from an ID or silently send an unquoted message.
Key takeaways
- Keep the current message ID, its parent ID, and its opaque reply token separate.
- Use the account and conversation from the selected message—not the latest message in the chat.
- The documented quoted-send operation is currently WhatsApp-only.
- History messages do not include reply tokens; decide explicitly whether a normal message is acceptable.
Which message will your reply quote?
Consider this hypothetical conversation: message A asks a question, message B quotes A and adds a correction, and your agent wants to quote B when responding.
A ← B ← your new response
The standard webhook reference distinguishes these values:
| Field on incoming B | Meaning | Application use |
|---|---|---|
data.message.id | B’s identity | Store and select B in your inbox |
data.message.reply_to_message_id | A’s identity | Display B’s parent relationship |
data.message.reply_token | Opaque handle for quoting B | Copy unchanged into the send request |
data.conversation.id and type | The originating chat | Populate the send destination |
account_id | The connected messaging account | Preserve the sending account |
Selecting A in your interface requires A’s own stored token. B’s parent ID is not a substitute. A parent may also be absent from your local store; display an unavailable reference rather than inventing its content.
This is different from deciding whether to send through an HTTP webhook response. The webhook response vs separate request comparison covers that transport question. Here, the question is which message the outgoing quote targets.
Build the request from the selected event
First authenticate and durably store the incoming event. Enable signing_secret and follow the webhook delivery contract: verify HMAC-SHA256 over the timestamp, a dot, and the raw body before trusting the payload. Timestamp freshness and deduplication remain separate checks; see the HMAC replay-protection tutorial.
The following JavaScript is a request builder, not a complete receiver or an automatic send loop. Its input is a validated, stored, real-time event selected by an authorized agent. The local function and error strings are application code, not API fields or service error codes.
function buildQuotedReply(event, text) {
const conversation = event.data?.conversation;
const message = event.data?.message;
if (event.type !== 'message.received' ||
event.provider !== 'whatsapp' ||
message?.direction !== 'inbound') {
throw new Error('Select an inbound WhatsApp message');
}
if (!event.account_id || !conversation?.id || !conversation.type) {
throw new Error('Missing destination context');
}
if (typeof message.reply_token !== 'string' || !message.reply_token) {
throw new Error('Quoted reply unavailable');
}
if (typeof text !== 'string' || !text.trim()) {
throw new Error('Reply text is required');
}
return {
account_id: event.account_id,
to: { id: conversation.id, type: conversation.type },
message: { type: 'text', text },
reply_to: { reply_token: message.reply_token }
};
}
Send the resulting body using POST /v1/messages with X-Api-Key, as specified in the quoted-reply endpoint reference. Keep the key on your backend. In a group, the conversation identifies the group; replacing it with data.sender.id changes the destination rather than selecting a quote.
Before dispatch, verify the operator can access the selected account and conversation. Save the selected message identity with the local sending task so a newly arrived message cannot change the target. Restrict access to stored tokens and avoid putting them in general logs or AI prompts.
Missing token, invalid token, or unsupported provider?
| Observation | Documented boundary | Recommended action |
|---|---|---|
| Real-time message has no token | WhatsApp tokens appear when reply-token signing is configured | Confirm the event source and configuration; offer an explicit normal-message choice |
Message came from conversation.history | History messages do not include reply_token | Do not construct a token or promise recovery from history |
400 invalid_reply_token | Token is altered, encrypted with a different key, or otherwise unreadable | Check the stored value and serialization; retain the error for investigation |
501 unsupported_by_provider | Quoted sending is not implemented for the selected provider | Disable this quoted-send path rather than retrying it indefinitely |
reply_to omitted | The request sends a normal message | Require an intentional fallback decision |
Webhook signing_secret authenticates delivery. Do not assume changing it repairs an unreadable encrypted reply handle. The error reference defines the returned errors; it does not promise token repair or a token lifetime.
Acceptance tests and scope
Test that selecting B quotes B, even when B quotes A. Also test a new message arriving during drafting, a group conversation, a missing token, a history-only message, and a repeated delivery. Repeated intake should not create another sending task. These are proposed tests, not reported results.
Record the actual send result. data.status: accepted is not a read receipt, and a network timeout is not proof that no message was sent. Do not resend blindly or remove reply_to automatically after an error.
UnifyPort provides an unofficial interface. A normalized event stream does not make quoted sending available on every channel. Telegram’s official Bot API reference defines its own reply_parameters and ReplyParameters; do not copy that schema into this request. Use the native API when your workflow needs its native capabilities. UnifyPort has no REST message-history read API or guaranteed replay of missed payloads.
FAQ
Can I put reply_to_message_id in reply_to.reply_token?
No. The former points to the incoming message’s parent; the latter must be the unchanged opaque token for the message you selected.
Can I quote a history message if I have its ID?
Not through this documented token-based operation without its token. History payloads do not provide one. Offer a normal message only as an explicit alternative.
Does this work for Telegram, LINE, or Zalo through UnifyPort?
The current quoted-send reference says WhatsApp only. Do not infer sending support from the presence of inbound reply relationships.
Next step and sources
Implement the selection checks against the quoted-reply reference before enabling your inbox’s Quote button.
Checked on 2026-09-26:
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.