Pipes.bot
BYON

Passthrough Messages

Send template messages, interactive buttons, lists, CTA URL buttons, and other advanced WhatsApp message types via the passthrough endpoint.

The passthrough endpoint forwards your message payload directly to Meta's Cloud API, giving you access to every WhatsApp message type — templates, interactive buttons, list messages, locations, contacts, and reactions.

POST /v1/messages/passthrough
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Both pool (pk_) and BYON (ak_) keys are accepted, but template messages require a BYON ak_ key.

Simple text messages

For sending plain text messages on BYON numbers, use the dedicated send endpoint:

POST /v1/messages/app/send
Authorization: Bearer ak_your_app_key
Content-Type: application/json
FieldTypeDescription
poolNumberIdstringRequired. Your BYON number ID
toNumberstringRequired. Recipient phone number
textstringRequired. Message text
curl -X POST https://api.pipes.bot/v1/messages/app/send \
  -H "Authorization: Bearer ak_your_app_key" \
  -H "Content-Type: application/json" \
  -d '{
    "poolNumberId": "pn_abc123",
    "toNumber": "5511999999999",
    "text": "Hello! Your order has been shipped."
  }'

The response uses the same format as passthrough:

{
  "messaging_product": "whatsapp",
  "contacts": [
    { "input": "5511999999999", "wa_id": "5511999999999" }
  ],
  "messages": [
    { "id": "wamid.abc123..." }
  ]
}

For templates, interactive messages, location, contacts, and reactions, use the passthrough endpoint below.

Routing fields

The routing fields depend on your API key type. These fields are stripped before forwarding to Meta.

BYON (ak_ key):

FieldTypeDescription
messaging_productstringRequired. Always "whatsapp"
poolNumberIdstringRequired. Your BYON number ID
tostringRequired. Recipient phone number
typestringRequired. Message type

Pool (pk_ key):

FieldTypeDescription
conversationIdstringRequired. Target conversation
typestringRequired. Message type

Sending template messages

Send a pre-approved template with dynamic parameters. Templates let you initiate conversations outside the 24-hour service window.

curl -X POST https://api.pipes.bot/v1/messages/passthrough \
  -H "Authorization: Bearer ak_your_app_key" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "type": "template",
    "poolNumberId": "pn_abc123",
    "to": "5511999999999",
    "template": {
      "name": "order_confirmation",
      "language": { "code": "en_US" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Maria" },
            { "type": "text", "text": "ORD-4521" },
            { "type": "text", "text": "January 20" }
          ]
        }
      ]
    }
  }'

Template with media header

{
  "messaging_product": "whatsapp",
  "type": "template",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "template": {
    "name": "shipping_update",
    "language": { "code": "en_US" },
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "image",
            "image": { "link": "https://example.com/tracking-map.jpg" }
          }
        ]
      },
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "ORD-4521" },
          { "type": "text", "text": "Jan 20, 2025" }
        ]
      }
    ]
  }
}

Template with button parameters

For templates with dynamic URL buttons, pass the variable portion:

{
  "messaging_product": "whatsapp",
  "type": "template",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "template": {
    "name": "order_tracking",
    "language": { "code": "en_US" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "ORD-4521" }
        ]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": "0",
        "parameters": [
          { "type": "text", "text": "ORD-4521" }
        ]
      }
    ]
  }
}

Interactive reply buttons

Send a message with up to 3 reply buttons. When the user taps a button, you receive a webhook with the button ID.

curl -X POST https://api.pipes.bot/v1/messages/passthrough \
  -H "Authorization: Bearer ak_your_app_key" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "type": "interactive",
    "poolNumberId": "pn_abc123",
    "to": "5511999999999",
    "interactive": {
      "type": "button",
      "header": {
        "type": "text",
        "text": "Confirm your appointment"
      },
      "body": {
        "text": "You have an appointment on Jan 20 at 3:00 PM. Would you like to confirm?"
      },
      "footer": {
        "text": "Reply within 24 hours"
      },
      "action": {
        "buttons": [
          {
            "type": "reply",
            "reply": { "id": "confirm", "title": "Confirm" }
          },
          {
            "type": "reply",
            "reply": { "id": "reschedule", "title": "Reschedule" }
          },
          {
            "type": "reply",
            "reply": { "id": "cancel", "title": "Cancel" }
          }
        ]
      }
    }
  }'

Button constraints

  • Max 3 buttons per message
  • Button title max 20 characters
  • Button id max 256 characters

Interactive list messages

Send a message with a selectable list of up to 10 items grouped into sections.

{
  "messaging_product": "whatsapp",
  "type": "interactive",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "interactive": {
    "type": "list",
    "header": {
      "type": "text",
      "text": "Our Menu"
    },
    "body": {
      "text": "Browse our menu and pick your items:"
    },
    "footer": {
      "text": "Prices may vary"
    },
    "action": {
      "button": "View Menu",
      "sections": [
        {
          "title": "Pizzas",
          "rows": [
            { "id": "margherita", "title": "Margherita", "description": "$12.00" },
            { "id": "pepperoni", "title": "Pepperoni", "description": "$14.00" }
          ]
        },
        {
          "title": "Drinks",
          "rows": [
            { "id": "cola", "title": "Cola", "description": "$3.00" },
            { "id": "water", "title": "Water", "description": "$2.00" }
          ]
        }
      ]
    }
  }
}

List constraints

  • Max 10 rows total across all sections
  • Max 10 sections
  • Row title max 24 characters
  • Row description max 72 characters
  • Action button text max 20 characters

Interactive CTA URL buttons

Send a message with a call-to-action button that opens a URL when tapped.

curl -X POST https://api.pipes.bot/v1/messages/passthrough \
  -H "Authorization: Bearer ak_your_app_key" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "type": "interactive",
    "poolNumberId": "pn_abc123",
    "to": "5511999999999",
    "interactive": {
      "type": "cta_url",
      "header": {
        "type": "text",
        "text": "Visit our store"
      },
      "body": {
        "text": "Check out our latest products and promotions."
      },
      "footer": {
        "text": "Free shipping on orders over $50"
      },
      "action": {
        "name": "cta_url",
        "parameters": {
          "display_text": "Shop Now",
          "url": "https://example.com/store"
        }
      }
    }
  }'

CTA URL fields

FieldTypeDescription
interactive.typestringMust be "cta_url"
interactive.headerobjectOptional. Text header
interactive.bodyobjectRequired. Message body text
interactive.footerobjectOptional. Footer text
interactive.action.namestringRequired. Must be "cta_url"
action.parameters.display_textstringRequired. Button label (max 20 characters)
action.parameters.urlstringRequired. HTTPS URL to open

CTA URL constraints

  • display_text max 20 characters
  • url must use HTTPS

Sending location messages

{
  "messaging_product": "whatsapp",
  "type": "location",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "location": {
    "latitude": "-23.5505",
    "longitude": "-46.6333",
    "name": "Pipes.bot HQ",
    "address": "Av. Paulista, 1000 - São Paulo"
  }
}

Sending contact cards

{
  "messaging_product": "whatsapp",
  "type": "contacts",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "contacts": [
    {
      "name": { "formatted_name": "Support Team", "first_name": "Support" },
      "phones": [
        { "phone": "+15551234567", "type": "WORK" }
      ],
      "emails": [
        { "email": "support@example.com", "type": "WORK" }
      ]
    }
  ]
}

Sending reactions

React to a received message with an emoji:

{
  "messaging_product": "whatsapp",
  "type": "reaction",
  "poolNumberId": "pn_abc123",
  "to": "5511999999999",
  "reaction": {
    "message_id": "wamid.abc123...",
    "emoji": "👍"
  }
}

To remove a reaction, send an empty emoji string.

Response

All passthrough responses are returned directly from Meta:

{
  "messaging_product": "whatsapp",
  "contacts": [
    { "input": "5511999999999", "wa_id": "5511999999999" }
  ],
  "messages": [
    { "id": "wamid.abc123..." }
  ]
}

Error handling

Meta errors are forwarded as-is. Common errors:

Meta error codeMeaning
131047Re-engagement message outside window
131026Message undeliverable (user not on WA)
131053Media upload failed
132000Template parameter count mismatch
132015Template is paused
132012Template parameter format mismatch

Pipes.bot-level errors:

StatusCodeDescription
400INVALID_REQUESTMissing type, routing fields, or invalid JSON
401UNAUTHORIZEDMissing or invalid API key
403BYON_ONLYTemplate messages require a BYON ak_ key
403WINDOW_CLOSEDService window expired (pool users only)
429RATE_LIMITED60 requests/minute exceeded
502META_UNREACHABLECould not reach Meta's API

Complete example

Send a template with buttons, then handle the button reply:

const API_KEY = "ak_your_app_key";
const BASE = "https://api.pipes.bot";

// Send a template message
const res = await fetch(`${BASE}/v1/messages/passthrough`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    messaging_product: "whatsapp",
    type: "template",
    poolNumberId: "pn_abc123",
    to: "5511999999999",
    template: {
      name: "appointment_reminder",
      language: { code: "en_US" },
      components: [
        {
          type: "body",
          parameters: [
            { type: "text", text: "Dr. Smith" },
            { type: "text", text: "January 20 at 3:00 PM" },
          ],
        },
      ],
    },
  }),
});

const result = await res.json();
console.log("Sent template:", result.messages?.[0]?.id);

When the user replies to a button, you receive a webhook event:

{
  "type": "whatsapp_message",
  "data": {
    "type": "interactive",
    "messageId": "wamid.xyz...",
    "fromNumber": "5511999999999",
    "interactive": {
      "type": "button_reply",
      "button_reply": {
        "id": "confirm",
        "title": "Confirm"
      }
    }
  }
}

Then send a follow-up interactive message:

await fetch(`${BASE}/v1/messages/passthrough`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    messaging_product: "whatsapp",
    type: "interactive",
    poolNumberId: "pn_abc123",
    to: "5511999999999",
    interactive: {
      type: "button",
      body: {
        text: "Your appointment is confirmed! Would you like a reminder?",
      },
      action: {
        buttons: [
          { type: "reply", reply: { id: "remind_1h", title: "1 hour before" } },
          { type: "reply", reply: { id: "remind_1d", title: "1 day before" } },
          { type: "reply", reply: { id: "no_remind", title: "No thanks" } },
        ],
      },
    },
  }),
});

On this page