# Peelo WhatsApp API Documentation

> Peelo is a WhatsApp Business API platform and an official **Meta Tech Provider**. Send and receive WhatsApp messages from your own code via a Node.js SDK (`npm install peelo`) or a plain HTTP REST API (`https://graph.peelo.io`).
>
> This page is the machine-readable version of https://console.peelo.io/#/documentation. Paste it into any LLM (ChatGPT, Claude, Gemini, Cursor, Copilot...) to get help integrating Peelo.

- Canonical Markdown URL: https://console.peelo.io/documentation.md
- llms.txt index: https://console.peelo.io/llms.txt
- Human-readable docs: https://console.peelo.io/#/documentation
- Create an account / get an API key: https://dashboard.peelo.io/register then https://console.peelo.io/#/api-keys
- SDK version: v2.0.0 (public beta)

---

## Table of Contents

1. [Introduction](#1-introduction)
2. [Meta Tech Provider: keep using the WhatsApp Business app](#2-meta-tech-provider)
3. [Authentication](#3-authentication)
4. [Node.js SDK](#4-nodejs-sdk)
    - [Installation](#installation)
    - [Initialization](#initialization)
    - [Sending text and media](#sending-messages)
    - [Interactive messages (buttons, lists, location request)](#interactive-messages)
    - [Templates (utility and marketing)](#templates)
    - [Typing indicator and reactions](#other-actions)
5. [HTTP REST API](#5-http-rest-api)
    - [Endpoint and headers](#endpoint-and-headers)
    - [JSON payload examples (cURL)](#json-payload-examples)
6. [Webhooks: receiving messages and status updates](#6-webhooks)
7. [Quick reference](#7-quick-reference)

---

## 1. Introduction

The Peelo API lets you manage WhatsApp Business conversations programmatically: send text, media, locations, contacts, interactive buttons and lists, and approved templates, and receive incoming messages and delivery statuses through webhooks.

Two integration options:

- **Node.js SDK** (`peelo` on npm): typed TypeScript client that handles rate limiting and request formatting for you. Recommended.
- **HTTP REST API**: a single JSON endpoint, usable from any language (PHP, Python, Go, Java, no-code tools, etc.).

**Rate limit:** the free tier is limited to **3 requests per second**. Upgrade to a paid plan to increase throughput.

**Phone number format:** always use the international format without the leading `+` (example: `221770000000` for a Senegalese number).

---

## 2. Meta Tech Provider

Peelo operates as an official Meta Tech Provider. This is different from a standard WhatsApp Business API integration (BSP).

Normally, when you migrate a phone number to the WhatsApp API you **lose access** to the WhatsApp Business mobile app. With Peelo **you keep that access**:

- **Hybrid use:** your support agents keep using the mobile app manually while your bot handles automated messages through the API.
- **No account deletion:** you do not need to delete your existing WhatsApp account to start using the API.
- **Zero downtime:** integration happens instantly, with no complex number migration.

---

## 3. Authentication

Every request to the Peelo API must carry your API key in the `x-api-key` HTTP header.

```http
x-api-key: pk_live_xxxxxxxxxxxxxxxxxxxxxx
```

- Generate and revoke keys in the console: https://console.peelo.io/#/api-keys
- Keys start with `pk_`.
- **Never expose your API key in client-side code** (browsers, mobile apps). Always call the API from your own backend.

---

## 4. Node.js SDK

The `peelo` npm package is the easiest way to use the API. It is written in TypeScript and ships full type definitions (autocomplete in modern IDEs).

### Installation

```bash
npm install peelo
# or: yarn add peelo / pnpm add peelo
```

### Initialization

```javascript
const { PeeloClient } = require('peelo');
// ESM / TypeScript: import { PeeloClient } from 'peelo';

// Find your API key in the "Api Keys" section of the console.
const client = new PeeloClient('pk_your_api_key_here');
```

The client instance is stateless and can be reused across your application.

### Sending Messages

All `send*` methods return a Promise. The first argument is always the recipient phone number in international format without `+`.

#### Text

Supports standard WhatsApp formatting (`*bold*`, `_italic_`, `~strikethrough~`, ```` ```monospace``` ````).

```javascript
await client.sendText('221770000000', 'Hello from Peelo SDK! *Bold text* supported.');
```

#### Image

Media must be a direct, publicly accessible URL.

```javascript
await client.sendImage(
  '221770000000',
  'https://example.com/image.jpg',
  'Check out this photo! 📸' // optional caption
);
```

#### Video

```javascript
await client.sendVideo(
  '221770000000',
  'https://example.com/video.mp4',
  'Watch this quick demo' // optional caption
);
```

#### Audio

```javascript
// Appears as a playable voice note when the format is supported
await client.sendAudio('221770000000', 'https://example.com/audio.mp3');
```

#### Document

```javascript
await client.sendDocument(
  '221770000000',
  'https://example.com/invoice.pdf',
  'invoice_october.pdf',              // filename shown to the user
  'Here is your invoice for October'  // optional caption
);
```

#### Location

```javascript
await client.sendLocation(
  '221770000000',
  14.6928,                 // latitude
  -17.4467,                // longitude
  'Statue of Renaissance', // name
  'Dakar, Senegal'         // address
);
```

#### Contacts

```javascript
const contacts = [{
  name: { formatted_name: 'Customer Service', first_name: 'Customer', last_name: 'Service' },
  phones: [{ phone: '+221770000000', type: 'WORK' }]
}];

await client.sendContacts('221770000000', contacts);
```

### Interactive Messages

Interactive messages let users pick predefined options instead of typing, which reduces errors and increases conversion.

#### Reply Buttons

Best for simple choices (1 to 3 buttons). The object keys are the button IDs you will receive back in the webhook, the values are the button labels.

```javascript
await client.sendReplyButtons('221770000000', 'Do you confirm your appointment?', {
  yes_btn: 'Yes, I confirm',
  no_btn: 'No, reschedule'
});
```

#### List Menu (select)

Best for longer option lists (up to 10 rows total), e.g. menus or categories.

```javascript
await client.sendList(
  '221770000000',
  'Welcome to Peelo Pizza',   // body text
  'View Menu',                // button text that opens the list
  [
    {
      title: 'Classic Pizzas',
      rows: [
        { id: 'marguerite', title: 'Marguerite', description: 'Tomato, Mozzarella' },
        { id: 'pepperoni', title: 'Pepperoni', description: 'Double spicy salami' }
      ]
    },
    {
      title: 'Drinks',
      rows: [
        { id: 'coke', title: 'Coca Cola', description: '33cl cold can' }
      ]
    }
  ],
  'Our Menu'                  // optional header
);
```

#### Location Request

Ask the user to share their current location.

```javascript
await client.sendLocationRequest('221770000000', 'Please share your location for delivery 📍');
```

### Templates

To start a conversation **outside the 24-hour customer service window**, you MUST use a pre-approved **template message** (created and approved in Meta Business Manager).

Peelo templates support **named parameters**: instead of numbered variables (`{{1}}`, `{{2}}`), map your data directly to the parameter names defined in Meta Business Manager. Positional arrays are also accepted.

#### Utility template (named parameters)

```javascript
// Template body: "Your order {{code}} will be delivered by {{driver}}."
const params = {
  code: 'ORD-12345',
  driver: 'Moussa Diop'
};

// 'en' is the language code of the approved template
await client.sendUtilityTemplate('221770000000', 'delivery_update', 'en', params);
```

#### Marketing template with header image

```javascript
await client.sendMarketingTemplate(
  '221770000000',
  'holiday_promo',                    // template name
  'en',                               // language
  'https://example.com/banner.jpg',   // header image URL
  {
    name: 'Sarah',                    // {{name}}
    discount: '20%'                   // {{discount}}
  }
);
```

### Other Actions

```javascript
// Typing indicator: shows "typing..." in reply to a received message (pass its wamid)
await client.sendTypingIndicator('221770000000', 'wamid.HBg...');

// Reaction: react to a message with an emoji
await client.sendReaction('221770000000', 'wamid.HBg...', '❤️');
```

---

## 5. HTTP REST API

If your stack is not Node.js, call the API directly with HTTP requests. The API is RESTful and accepts JSON payloads. Message payloads follow the same shape as the Meta WhatsApp Cloud API `messages` objects.

### Endpoint and headers

| Item | Value |
|---|---|
| Base URL | `https://graph.peelo.io` |
| Send a message | `POST /api/send-message` |
| Auth header | `x-api-key: pk_your_api_key` |
| Content type | `Content-Type: application/json` |

Common body fields:

- `to` (string, required): recipient phone number, international format without `+`.
- `type` (string, required): `text`, `image`, `video`, `audio`, `document`, `location`, `contacts`, `interactive`, `template`.
- One object named after `type` carrying the content (see examples below).

### JSON payload examples

Replace `pk_your_api_key` with your real key.

#### Text message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "text",
    "text": { "body": "Hello World!" }
  }'
```

#### Image message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "image",
    "image": {
      "link": "https://example.com/image.jpg",
      "caption": "Check out this photo!"
    }
  }'
```

#### Video message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "video",
    "video": {
      "link": "https://example.com/video.mp4",
      "caption": "Watch this demo"
    }
  }'
```

#### Audio message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "audio",
    "audio": { "link": "https://example.com/audio.mp3" }
  }'
```

#### Document message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "document",
    "document": {
      "link": "https://example.com/invoice.pdf",
      "filename": "invoice_october.pdf",
      "caption": "Here is your invoice"
    }
  }'
```

#### Location message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "location",
    "location": {
      "latitude": 14.6928,
      "longitude": -17.4467,
      "name": "Statue of Renaissance",
      "address": "Dakar, Senegal"
    }
  }'
```

#### Contacts message

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "contacts",
    "contacts": [
      {
        "name": { "formatted_name": "Customer Service", "first_name": "Customer", "last_name": "Service" },
        "phones": [{ "phone": "+221770000000", "type": "WORK" }]
      }
    ]
  }'
```

#### Interactive reply buttons

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "interactive",
    "interactive": {
      "type": "button",
      "body": { "text": "Are you satisfied?" },
      "action": {
        "buttons": [
          { "type": "reply", "reply": { "id": "yes", "title": "Yes" } },
          { "type": "reply", "reply": { "id": "no", "title": "No" } }
        ]
      }
    }
  }'
```

#### Interactive list

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "interactive",
    "interactive": {
      "type": "list",
      "header": { "type": "text", "text": "Menu" },
      "body": { "text": "Please select an option" },
      "action": {
        "button": "View Options",
        "sections": [
          {
            "title": "Section 1",
            "rows": [
              { "id": "opt1", "title": "Option 1", "description": "Description 1" }
            ]
          }
        ]
      }
    }
  }'
```

#### Template message (named parameters)

```bash
curl -X POST https://graph.peelo.io/api/send-message \
  -H "x-api-key: pk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "221770000000",
    "type": "template",
    "template": {
      "name": "delivery_update",
      "language": { "code": "en" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "parameter_name": "code", "text": "ORD-12345" },
            { "type": "text", "parameter_name": "driver", "text": "Moussa Diop" }
          ]
        }
      ]
    }
  }'
```

For templates with positional variables (`{{1}}`, `{{2}}`), omit `parameter_name` and keep the parameters in order.

---

## 6. Webhooks

To receive incoming messages and status updates (sent, delivered, read), configure a webhook URL in the console: https://console.peelo.io/#/webhooks

### Step 1: verification challenge (GET)

When you save your webhook URL, Peelo sends a `GET` request to verify that you own the endpoint, with these query parameters:

- `hub.mode=subscribe`
- `hub.verify_token=<the token you set in the console>`
- `hub.challenge=<random string>`

Your server MUST respond `200` with the raw `hub.challenge` value as plain text.

```javascript
// Express.js example
app.get('/webhook', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === 'MY_SECURE_TOKEN') {
    console.log('Webhook verified!');
    res.status(200).send(challenge);
  } else {
    res.sendStatus(403);
  }
});
```

### Step 2: receiving events (POST)

Once verified, Peelo sends `POST` requests with a JSON body for each event. The payload uses the Meta WhatsApp Cloud API webhook format. Example incoming text message:

```json
{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "WABA_ID",
      "changes": [
        {
          "field": "messages",
          "value": {
            "messaging_product": "whatsapp",
            "metadata": {
              "display_phone_number": "221770000000",
              "phone_number_id": "PHONE_NUMBER_ID"
            },
            "contacts": [{ "profile": { "name": "Customer" }, "wa_id": "221770000000" }],
            "messages": [
              {
                "from": "221770000000",
                "id": "wamid.HBgM...",
                "timestamp": "1700000000",
                "type": "text",
                "text": { "body": "Hello" }
              }
            ]
          }
        }
      ]
    }
  ]
}
```

Handler sketch:

```javascript
app.post('/webhook', express.json(), (req, res) => {
  res.sendStatus(200); // acknowledge quickly

  const value = req.body?.entry?.[0]?.changes?.[0]?.value;
  for (const msg of value?.messages ?? []) {
    const from = msg.from;              // sender phone number
    if (msg.type === 'text') {
      console.log('Text:', msg.text.body);
    } else if (msg.type === 'interactive') {
      // button reply: msg.interactive.button_reply.id
      // list reply:   msg.interactive.list_reply.id
    }
  }
  for (const status of value?.statuses ?? []) {
    // status.status: 'sent' | 'delivered' | 'read' | 'failed', status.id: wamid
  }
});
```

You can test your webhook without a real phone using the Chat Simulator in the console: https://console.peelo.io/#/chat-simulator

---

## 7. Quick reference

| Task | SDK method | HTTP `type` |
|---|---|---|
| Text | `client.sendText(to, body)` | `text` |
| Image | `client.sendImage(to, url, caption?)` | `image` |
| Video | `client.sendVideo(to, url, caption?)` | `video` |
| Audio | `client.sendAudio(to, url)` | `audio` |
| Document | `client.sendDocument(to, url, filename, caption?)` | `document` |
| Location | `client.sendLocation(to, lat, lng, name, address)` | `location` |
| Contacts | `client.sendContacts(to, contacts[])` | `contacts` |
| Reply buttons | `client.sendReplyButtons(to, body, { id: label })` | `interactive` (`button`) |
| List menu | `client.sendList(to, body, buttonText, sections[], header?)` | `interactive` (`list`) |
| Location request | `client.sendLocationRequest(to, body)` | `interactive` |
| Utility template | `client.sendUtilityTemplate(to, name, lang, params)` | `template` |
| Marketing template | `client.sendMarketingTemplate(to, name, lang, headerImageUrl, params)` | `template` |
| Typing indicator | `client.sendTypingIndicator(to, wamid)` | n/a |
| Reaction | `client.sendReaction(to, wamid, emoji)` | n/a |

Key facts an integrator needs:

- Base URL `https://graph.peelo.io`, endpoint `POST /api/send-message`, header `x-api-key`.
- Phone numbers: international format, no `+`.
- Free tier: 3 requests/second.
- Outside the 24h window: templates only.
- Webhooks: Meta-style `hub.challenge` verification, Meta Cloud API payload format.
- Support and sign-up: https://peelo.io
