Pipes.bot

Sending Replies

Reply to WhatsApp messages through the WebSocket connection and REST API.

You can send replies through the WebSocket connection (text only) or the REST API (text and media).

Replying via WebSocket

Send a whatsapp_reply message through the open connection:

{
  "type": "whatsapp_reply",
  "data": {
    "conversationId": "conv_xyz789",
    "poolNumberId": "pool_123",
    "toNumber": "+15559876543",
    "text": "Thanks for reaching out!"
  }
}

Reply fields

FieldTypeDescription
conversationIdstringConversation ID from the incoming message
poolNumberIdstring?Ignored for routing. The server always routes via the conversation's stored pool number. Safe to omit
toNumberstringRecipient's WhatsApp number
textstringReply text (max 4096 characters)

Reply confirmation

After sending a reply, the server responds with a reply_status event:

Success:

{
  "type": "reply_status",
  "data": {
    "conversationId": "conv_xyz789",
    "success": true,
    "messageId": "wamid.abc123..."
  }
}

Failure:

{
  "type": "reply_status",
  "data": {
    "conversationId": "conv_xyz789",
    "success": false,
    "error": "Service window expired — last inbound message is older than 24 hours.",
    "code": "SERVICE_WINDOW_EXPIRED"
  }
}

reply_status failures can carry these codes:

CodeMeaning
SERVICE_WINDOW_EXPIREDLast inbound is older than 24 hours (non-BYON only)
CONVERSATION_SCOPE_MISMATCHReply targets a conversation outside a conversation-scoped connection
POOL_NUMBER_SCOPE_MISMATCHReply targets a conversation on a different pool number than the connection's scope

A Meta-side send failure returns a reply_status with an error message but no code.

Reply scoping

Reply behavior depends on your connection scope:

  • User-scoped connections can reply to any conversation you own
  • Pool-number-scoped connections can only reply to conversations belonging to the scoped pool number — replies targeting a conversation on a different pool number are rejected
  • Conversation-scoped connections can only reply to the conversation they are scoped to — replies targeting a different conversationId are rejected

Replying via REST API

For text and media replies, use the Messages API:

POST /v1/messages/send
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
  "conversationId": "conv_xyz789",
  "text": "Here's the info you requested"
}

The REST API is not affected by connection scoping — you can send to any conversation you own regardless of your WebSocket connections.

See Media Handling for sending images, audio, video, and documents.

24-hour service window

WhatsApp enforces a 24-hour messaging window. You can only send outbound messages within 24 hours of the customer's last inbound message. After the window expires:

  • WebSocket replies receive a reply_status with code: "SERVICE_WINDOW_EXPIRED"
  • REST API replies receive HTTP 403 with code: "SERVICE_WINDOW_EXPIRED"

The window resets each time the customer sends a new message.

BYON numbers are exempt from the platform-side 24-hour window check — replies on BYON conversations always pass it.

Conversation ownership

Replies are validated against conversation ownership. You can only reply to conversations that belong to your account. Attempting to reply to another tenant's conversation is silently rejected.

Example: echo bot

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());

  if (event.type === "whatsapp_message") {
    const msg = event.data;

    // Echo text messages back
    if (msg.type === "text") {
      ws.send(JSON.stringify({
        type: "whatsapp_reply",
        data: {
          conversationId: msg.conversationId,
          poolNumberId: msg.poolNumberId,
          toNumber: msg.fromNumber,
          text: `You said: ${msg.body}`,
        },
      }));
    }
  }

  if (event.type === "reply_status") {
    if (!event.data.success) {
      console.error(`Reply failed: ${event.data.error}`);
    }
  }
});

On this page