> ## Documentation Index
> Fetch the complete documentation index at: https://zapo.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Raw proto sends

> Send content types WhatsApp supports but zapo doesn't have a typed builder for yet — locations, contact vCards, group invites, buttons, list menus, interactive native flow, products, orders, newsletter admin invites, ephemeral toggles, phone-number requests, and Business PIX / review-and-pay payment cards — as raw Proto.IMessage payloads.

For content types WhatsApp supports but zapo doesn't wrap in a typed builder yet, `client.message.send` also accepts a raw `Proto.IMessage`. Fill the field that names the content type — `locationMessage`, `contactMessage`, `contactsArrayMessage`, `interactiveMessage`, and so on — and the library encodes it verbatim.

Kinds that already have a typed builder — polls, reactions, edits, revokes, pins, keep-in-chat, view-once wrapping, and quotes / mentions / link previews — belong in [Sending messages](/en/guides/sending-messages) and [Interactive messages](/en/guides/interactive-messages). This page is for the raw-only kinds.

The full set of recognized `Proto.IMessage` fields (location, live location, contacts, group invite, product, order, …) is listed in the [message types reference](/en/reference/message-types). Some examples below use enum values from the `proto` namespace:

```ts theme={null}
import { proto } from 'zapo-js'
```

## Locations

```ts theme={null}
await client.message.send(jid, {
  locationMessage: {
    degreesLatitude: -23.5613,
    degreesLongitude: -46.6565,
    name: 'Av. Paulista',
    address: 'São Paulo, BR'
  }
})
```

`name` and `address` are optional.

For a live-location message, use the `liveLocationMessage` field instead — it carries movement metadata (`accuracyInMeters`, `speedInMps`, `sequenceNumber`).

```ts theme={null}
await client.message.send(jid, {
  liveLocationMessage: {
    degreesLatitude: -23.5505,
    degreesLongitude: -46.6333,
    accuracyInMeters: 50,
    speedInMps: 0,
    caption: 'On my way',
    sequenceNumber: 1
  }
})
```

## Contacts

A single contact card is a `contactMessage` with a vCard string:

```ts theme={null}
const vcard = [
  'BEGIN:VCARD',
  'VERSION:3.0',
  'FN:Jeff Singh',
  'TEL;type=CELL;type=VOICE;waid=5511999999999:+55 11 99999-9999',
  'END:VCARD'
].join('\n')

await client.message.send(jid, {
  contactMessage: { displayName: 'Jeff', vcard }
})
```

The `waid=<digits>` parameter on the `TEL` line is what lets the WhatsApp client link the card back to a WhatsApp account — use the recipient's E.164 phone number without the `+`.

For multiple cards at once, use `contactsArrayMessage`:

```ts theme={null}
await client.message.send(jid, {
  contactsArrayMessage: {
    displayName: '2 contacts',
    contacts: [
      { displayName: 'Jeff', vcard },
      { displayName: 'Jane', vcard: janeVcard }
    ]
  }
})
```

## Group invite

```ts theme={null}
await client.message.send(jid, {
  groupInviteMessage: {
    groupJid: '123456789-987654@g.us',
    inviteCode: 'AbCdEf123',
    inviteExpiration: Math.floor(Date.now() / 1000) + 86_400,
    groupName: 'My group',
    caption: 'Join us!'
  }
})
```

## Buttons

Up to three quick-reply buttons. The header is a `oneof` — pick text, image, video, location, or document (pre-uploaded for media):

```ts theme={null}
await client.message.send(jid, {
  buttonsMessage: {
    contentText: 'Order placed — what next?',
    footerText: 'Reply within 24h',
    headerType: proto.Message.ButtonsMessage.HeaderType.TEXT,
    text: 'Order #1234',
    buttons: [
      {
        buttonId: 'track',
        buttonText: { displayText: 'Track' },
        type: proto.Message.ButtonsMessage.Button.Type.RESPONSE
      },
      {
        buttonId: 'cancel',
        buttonText: { displayText: 'Cancel' },
        type: proto.Message.ButtonsMessage.Button.Type.RESPONSE
      }
    ]
  }
})
```

## List menu

A single-select list of rows grouped into sections:

```ts theme={null}
await client.message.send(jid, {
  listMessage: {
    title: 'Menu',
    description: 'Choose an item',
    buttonText: 'View menu',
    footerText: 'Open 9–18',
    listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
    sections: [
      {
        title: 'Pizzas',
        rows: [
          { rowId: 'pizza-margherita', title: 'Margherita', description: 'Tomato, mozzarella, basil' },
          { rowId: 'pizza-pepperoni',  title: 'Pepperoni',  description: 'Tomato, cheese, pepperoni' }
        ]
      },
      {
        title: 'Drinks',
        rows: [{ rowId: 'drink-cola', title: 'Cola' }]
      }
    ]
  }
})
```

## Interactive native flow (cta\_url)

The modern interactive surface — buttons whose params are JSON-encoded ad-hoc payloads:

```ts theme={null}
await client.message.send(jid, {
  interactiveMessage: {
    body: { text: 'Tap below to open the form' },
    footer: { text: 'Powered by your bot' },
    nativeFlowMessage: {
      buttons: [
        {
          name: 'cta_url',
          buttonParamsJson: JSON.stringify({
            display_text: 'Open form',
            url: 'https://example.com/form'
          })
        }
      ],
      messageVersion: 1
    }
  }
})
```

This is the same wire shape as the PIX / review-and-pay cards in [Payments](#payments-pix--review-and-pay) below — only the button `name` and `buttonParamsJson` differ.

## Product

Send a catalog product. The inner `productImage` must already be uploaded:

```ts theme={null}
await client.message.send(jid, {
  productMessage: {
    businessOwnerJid: '5511999999999@s.whatsapp.net',
    body: 'Take a look at this',
    footer: 'In stock',
    product: {
      productId: '12345',
      title: 'Hat',
      description: 'One size, adjustable',
      currencyCode: 'BRL',
      priceAmount1000: 49_900, // 49.90 BRL — price × 1000
      retailerId: 'sku-001',
      url: 'https://example.com/p/12345',
      productImage: { /* pre-uploaded image fields */ }
    }
  }
})
```

## Order

Order confirmation / inquiry:

```ts theme={null}
await client.message.send(jid, {
  orderMessage: {
    orderId: 'ord-abc',
    orderTitle: 'Sample order',
    itemCount: 3,
    status: proto.Message.OrderMessage.OrderStatus.INQUIRY,   // or ACCEPTED / DECLINED
    surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
    sellerJid: '5511888888888@s.whatsapp.net',
    totalAmount1000: 149_700, // 149.70 BRL — total × 1000
    totalCurrencyCode: 'BRL',
    message: 'Order details'
  }
})
```

## Newsletter admin invite

Invite a contact to co-admin one of your newsletters:

```ts theme={null}
await client.message.send(contactJid, {
  newsletterAdminInviteMessage: {
    newsletterJid: '120363xxxxxxxxxxxxxx@newsletter',
    newsletterName: 'My Newsletter',
    caption: 'Become a co-admin',
    inviteExpiration: Math.floor(Date.now() / 1000) + 7 * 86_400
  }
})
```

## Toggle disappearing messages (ephemeral setting)

Chat-wide toggle for disappearing messages — distinct from the per-message [`expirationSeconds` send option](/en/guides/sending-messages#send-options-reference) (one message) and the [`ephemeralMessage` wrapper](/en/reference/message-types#disappearing-wrapper-ephemeralmessage) (one message inheriting the chat timer). This one flips the timer for the whole chat.

```ts theme={null}
await client.message.send(jid, {
  protocolMessage: {
    type: proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
    ephemeralExpiration: 7 * 24 * 3600 // seconds; 0 disables
  }
})
```

## Request a phone number

```ts theme={null}
await client.message.send(jid, { requestPhoneNumberMessage: {} })
```

## Payments (PIX & review-and-pay)

Send WhatsApp Business payment cards — static PIX (`payment_info`) and order checkout (`review_and_pay`) — as raw `interactiveMessage` / `nativeFlowMessage` payloads through `client.message.send`. There is no typed builder for them yet, so build the shape yourself the same way as the locations/contacts cards above. The library relays the interactive native-flow buttons; the WhatsApp clients render the card UI.

<Warning>
  Payment cards are a **Business / native-flow** feature. Rendering differs between WhatsApp mobile and WhatsApp Web — prefer the flow that matches the UI you want (`payment_info` for a PIX-only card, `review_and_pay` for the "Nº da cobrança" / order card) and verify on both clients.
</Warning>

Amounts are integer minor units plus an `offset` divisor: `{ value: 1000, offset: 100 }` renders as **R\$ 10,00**. PIX `key_type` is one of `EVP` (chave aleatória), `EMAIL`, `PHONE` (E.164 preferred), `CPF`, `CNPJ`.

### PIX card (`payment_info`)

Renders the **PIX payment** card (key / merchant). Use this for a static PIX key without an order card.

```ts theme={null}
await client.message.send(jid, {
  interactiveMessage: {
    nativeFlowMessage: {
      messageVersion: 1,
      buttons: [
        {
          name: 'payment_info',
          buttonParamsJson: JSON.stringify({
            currency: 'BRL',
            total_amount: { value: 0, offset: 100 },
            reference_id: `PIX${Date.now()}`,
            type: 'physical-goods',
            order: {
              status: 'pending',
              subtotal: { value: 0, offset: 100 },
              order_type: 'ORDER',
              items: [
                { name: '', amount: { value: 0, offset: 100 }, quantity: 0, sale_amount: { value: 0, offset: 100 } }
              ]
            },
            payment_settings: [
              {
                type: 'pix_static_code',
                pix_static_code: {
                  merchant_name: 'Loja Exemplo',
                  key: 'pix@loja.com',
                  key_type: 'EMAIL'
                }
              }
            ],
            share_payment_status: false,
            is_soft_deleted: false,
            referral: 'chat_attachment'
            // display_text: 'Pagar com PIX' // optional
          })
        }
      ]
    }
  }
})
```

### Review-and-pay card (`review_and_pay`)

Renders the **order / cobrança** card (reference number, items, total). Use this for a checkout-style summary.

```ts theme={null}
await client.message.send(jid, {
  interactiveMessage: {
    body: { text: 'Olá! Sua fatura está disponível.' },
    footer: { text: 'Se já pagou, desconsidere.' },
    nativeFlowMessage: {
      messageVersion: 1,
      buttons: [
        {
          name: 'review_and_pay',
          buttonParamsJson: JSON.stringify({
            currency: 'BRL',
            reference_id: 'PGT-PIX-001',
            type: 'physical-goods',
            total_amount: { value: 10000, offset: 100 }, // R$ 100,00
            payment_settings: [
              {
                type: 'pix_static_code',
                pix_static_code: {
                  merchant_name: 'Loja Exemplo',
                  key: 'pix@loja.com',
                  key_type: 'EMAIL'
                }
              }
            ],
            order: {
              status: 'payment_requested',
              subtotal: { value: 10000, offset: 100 },
              order_type: 'ORDER',
              items: [
                { name: 'Fatura', amount: { value: 10000, offset: 100 }, quantity: 1 }
              ]
              // discount: { value: 500, offset: 100 }
            }
            // additional_note: 'Pagamento até o vencimento'
          })
        }
      ]
    }
  }
})
```

`buttonParamsJson` **must** be a JSON string — build the object in code and stringify it. `body` / `footer` on `interactiveMessage` are optional. Don't mix `payment_info` and `review_and_pay` expecting the same UI — they render different cards, and adding extra `cta_copy` / CTA buttons in the same `nativeFlowMessage` can render differently on mobile vs Web.

## See also

* [Sending messages](/en/guides/sending-messages) — the base `client.message.send` API, options, and typed content variants.
* [Interactive messages](/en/guides/interactive-messages) — typed builders for polls, reactions, edits, revokes, pins, and keep-in-chat.
* [Message types reference](/en/reference/message-types) — every recognized `Proto.IMessage` field and its resolved type.
