← All posts
Tutorial

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 BMeaningApplication use
data.message.idB’s identityStore and select B in your inbox
data.message.reply_to_message_idA’s identityDisplay B’s parent relationship
data.message.reply_tokenOpaque handle for quoting BCopy unchanged into the send request
data.conversation.id and typeThe originating chatPopulate the send destination
account_idThe connected messaging accountPreserve 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?

ObservationDocumented boundaryRecommended action
Real-time message has no tokenWhatsApp tokens appear when reply-token signing is configuredConfirm the event source and configuration; offer an explicit normal-message choice
Message came from conversation.historyHistory messages do not include reply_tokenDo not construct a token or promise recovery from history
400 invalid_reply_tokenToken is altered, encrypted with a different key, or otherwise unreadableCheck the stored value and serialization; retain the error for investigation
501 unsupported_by_providerQuoted sending is not implemented for the selected providerDisable this quoted-send path rather than retrying it indefinitely
reply_to omittedThe request sends a normal messageRequire 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:

UnifyPort API

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.