# Guide to Green API Webhooks: Features and Best Practices

Green API connects a WhatsApp account to an HTTP API. Each connected account is an instance, and the instance reports what happens on it: incoming messages, messages sent from the phone or the API, delivery and read statuses, calls, and changes to the instance's own state. Green API can deliver those notifications by calling your server, which it calls Webhook Endpoint technology.

This guide covers how to configure an instance's webhook URL and token, how Green API builds the `Authorization` header, the webhook types and payloads, how delivery and retries work, and the best practices for running a Green API receiver in production.

## What are Green API webhooks?

Green API offers two ways to receive notifications. With the HTTP API, you poll with `ReceiveNotification` and acknowledge with `DeleteNotification`. With Webhook Endpoint technology, Green API calls your URL as soon as a notification is ready. Green API describes the second as the fastest, with capacity "limited only by the rate of processing webhooks on your server side".

Each instance has one `webhookUrl`, an optional `webhookUrlToken`, and a set of switches that decide which webhook types it sends. There's no signature. If you set a token, Green API sends it in the `Authorization` header of every request, and your server checks it.

## Green API webhook features

| Feature | Details |
| --- | --- |
| Configuration | The `SetSettings` method, or the instance's settings in the console |
| URL | One `webhookUrl` per instance |
| Subscription model | Per-type switches such as `incomingWebhook`, `outgoingWebhook` and `stateWebhook` |
| Method and format | `POST`, `Content-Type: application/json; charset=utf-8` |
| Event identifier | `typeWebhook` in the body |
| Signature | None |
| Authentication | `webhookUrlToken`, sent in the `Authorization` header |
| Retries | Every minute until delivered |
| Delivery guarantee | Within 24 hours |
| Throughput | About 50 notifications a second |
| Source IPs | 13 published addresses |
| Alternative | Polling with `ReceiveNotification` and `DeleteNotification` |

## Common events

The body's `typeWebhook` field names the webhook type:

| `typeWebhook` | Sent when |
| --- | --- |
| `incomingMessageReceived` | A message or file arrives |
| `outgoingMessageReceived` | A message is sent from the phone |
| `outgoingAPIMessageReceived` | A message is sent through the API |
| `outgoingMessageStatus` | An outgoing message's status changes |
| `incomingMessageStatus` | An incoming message's status changes |
| `stateInstanceChanged` | The instance's authorisation state changes |
| `incomingCall` | A call comes in |
| `outgoingCall` | A call is made |
| `quotaExceeded` | Developer plan limits are exceeded |
| `statusInstanceChanged` | The instance's socket state changes (archived) |
| `deviceInfo` | The phone's device and battery level are reported (archived) |
| `incomingBlock` | A chat is added to the blocked contacts list (archived) |

Every body starts with the same envelope: `typeWebhook`, `instanceData` (`idInstance`, `wid` and `typeInstance`) and `timestamp` in Unix seconds. An incoming text message:

```json
{
  "typeWebhook": "incomingMessageReceived",
  "instanceData": {
    "idInstance": 7103000000,
    "wid": "79876543210@c.us",
    "typeInstance": "whatsapp"
  },
  "timestamp": 1588091580,
  "idMessage": "F7AEC1B7086ECDC7E6E45923F5EDB825",
  "senderData": {
    "chatId": "79001234567@c.us",
    "sender": "79001234567@c.us",
    "chatName": "John",
    "senderName": "John",
    "senderContactName": "John Doe"
  },
  "messageData": {
    "typeMessage": "textMessage",
    "textMessageData": {
      "textMessage": "I use Green-API to send this message to you!"
    }
  }
}

```

`messageData.typeMessage` says what kind of message it is, and the matching object holds the content. Green API documents separate formats for text, quoted, media, location, contact, poll, reaction, edited and deleted messages, and more.

A status update for a message you sent:

```json
{
  "typeWebhook": "outgoingMessageStatus",
  "chatId": "79001234567@c.us",
  "instanceData": {
    "idInstance": 7103000000,
    "wid": "79876543210@c.us",
    "typeInstance": "whatsapp"
  },
  "timestamp": 1727691478,
  "idMessage": "3EB0608D6A2901063D63",
  "status": "sent",
  "sendByApi": true
}

```

`status` can be `sent`, `delivered`, `read`, `failed`, `noAccount`, `notInGroup` or `suspended`, and the deprecated `yellowCard`, which `suspended` replaces. Green API says `failed` and `noAccount` can't be switched off in `SetSettings`, so your handler must process them.

## Setting up Green API webhooks

Call `SetSettings` for the instance with your URL, an optional token and the types you want:

```json
{
  "webhookUrl": "https://example.com/webhooks/green-api",
  "webhookUrlToken": "Bearer 3f6b0c2e9a7d4c1b8e5f2a6d9c0b3e7f",
  "incomingWebhook": "yes",
  "outgoingWebhook": "yes",
  "outgoingMessageWebhook": "yes",
  "stateWebhook": "yes",
  "deviceWebhook": "no"
}

```

Or open the instance in the console. Once the instance is authorised, its webhook settings show the URL, a switch per webhook type and the **Webhook URL Token**. If the settings don't appear, the instance isn't authorised yet.

## Securing Green API webhooks

Set `webhookUrlToken` to a long random value. Green API's rules for the header it sends are:

* If you set `Basic <token>`, the header is `Authorization: Basic <token>`.
* If you set `Bearer <token>`, the header is `Authorization: Bearer <token>`.
* If you set the token without a type, the header defaults to `Bearer <token>`.

Setting the type explicitly removes any doubt. Compare the whole header value in constant time:

```javascript
const crypto = require('crypto');
const express = require('express');

function verifyHeader(value, expected) {
  if (typeof value !== 'string' || !expected) return false;
  const received = Buffer.from(value, 'utf8');
  const wanted = Buffer.from(expected, 'utf8');
  return received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
}

const app = express();

app.post('/webhooks/green-api', express.raw({ type: '*/*' }), (req, res) => {
  const token = process.env.GREEN_API_WEBHOOK_TOKEN; // the value after "Bearer "
  if (!token) return res.sendStatus(500); // fail closed

  if (!verifyHeader(req.headers['authorization'], `Bearer ${token}`)) {
    return res.sendStatus(401);
  }

  const notification = JSON.parse(req.body.toString('utf8'));
  // Route on notification.typeWebhook, then process asynchronously.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import hmac
import os

def verify_green_api_header(value) -> bool:
    token = os.environ.get("GREEN_API_WEBHOOK_TOKEN")
    if not token or not value:
        return False
    return hmac.compare_digest(value.encode(), f"Bearer {token}".encode())

```

You can also allow only Green API's published source addresses in your firewall: `46.101.109.139`, `51.250.12.167`, `51.250.84.44`, `51.250.95.149`, `89.169.137.216`, `158.160.49.84`, `165.22.93.202`, `167.172.162.71`, `104.248.252.93`, `158.160.139.176`, `64.226.111.11`, `207.154.255.195` and `94.131.81.171`.

> **Make Green API webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks the `Authorization` header on every Green API notification, queues it the moment it arrives, and delivers it to your service at a pace it can handle.

## Green API webhook limitations and pain points

### A slow server delays every notification

**The Problem:** Messages reach your application minutes late during busy periods.

**Why It Happens:** Green API retries failed deliveries at a "1-minute interval", and sends about 50 notifications a second. If your server is down or slow, notifications queue behind each other, and each failure costs at least a minute.

**Workarounds:**

* Return `200` as soon as the notification is stored, and process it afterwards.
* Keep the receiving server separate from slow work such as AI replies or CRM writes.
* Use Green API's queue methods to see how many webhooks are waiting.

**How Hookdeck Can Help:** Hookdeck accepts each notification immediately, so Green API's queue keeps moving, and then delivers to your service with its own rate limits and retries.

### Delivery is guaranteed for 24 hours only

**The Problem:** After a long outage, some notifications never arrive.

**Why It Happens:** Green API says "the delivery of incoming webhooks is guaranteed within 24 hours". Beyond that, there's no promise.

**Workarounds:**

* Monitor your endpoint and fix outages quickly.
* After an outage, check the incoming and outgoing message journals for anything you missed.

**How Hookdeck Can Help:** Hookdeck stores every notification it receives, so your service can be down for longer than a day and still replay everything once it recovers.

### The token is static and unsigned

**The Problem:** Anyone who learns your URL and token can send fake messages to your bot.

**Why It Happens:** Green API authenticates with a fixed `Authorization` header and doesn't sign the body.

**Workarounds:**

* Use a long random token, and change it with `SetSettings` if it might have leaked.
* Restrict the endpoint to Green API's published IP addresses.
* Serve the endpoint over HTTPS only.

**How Hookdeck Can Help:** Hookdeck's Green API source type compares the whole `Authorization` header value. Enter it exactly as Green API sends it, which is your `webhookUrlToken` as you set it, with `Bearer ` in front if you set it without a type. Verification is optional on the source, so leave it off if you didn't set a token.

### Statuses and duplicates

**The Problem:** You count the same message twice, or a late `delivered` status overwrites `read`.

**Why It Happens:** If you process a notification but Green API doesn't see your success response, it sends it again a minute later. Each status change for a message is also its own webhook, and retries can change the order they reach you.

**Workarounds:**

* Deduplicate on `typeWebhook`, `idMessage` and, for statuses, `status`.
* Treat `read` as later than `delivered`, and `delivered` as later than `sent`, whatever order they arrive in.

**How Hookdeck Can Help:** Hookdeck's deduplication can drop repeated notifications based on body fields such as `idMessage` and `status` before they reach your service.

## Best practices

### Compare the whole header in constant time

Check the full `Authorization` value, scheme included, with `crypto.timingSafeEqual` (after a length check) or `hmac.compare_digest`. Fail closed when the token isn't configured.

### Enable only the types you use

Every switch you turn on adds traffic. Leave `deviceWebhook` and other types you don't handle off, but always handle `failed` and `noAccount` statuses, which can't be turned off.

### Route on `typeWebhook`, then `typeMessage`

Branch on `typeWebhook` first and on `messageData.typeMessage` second. Log and skip types you don't recognise rather than failing the request.

### Use `instanceData.idInstance` when you run several instances

If several WhatsApp numbers share one endpoint, `idInstance` tells you which one a notification belongs to. Give each instance its own token if you want to tell them apart securely.

### Acknowledge first, process after

Store the notification, return `200`, and reply to messages from a worker. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks) and our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

## Conclusion

Green API sends each WhatsApp notification for an instance to its `webhookUrl`, identified by `typeWebhook` and authenticated by the `webhookUrlToken` in the `Authorization` header rather than a signature. Set the token with an explicit type, compare the whole header in constant time, answer quickly so the one-minute retries don't build a backlog, and deduplicate on `idMessage`.

[Hookdeck Event Gateway](https://hookdeck.com) checks each Green API notification, stores it, and retries into your service on your schedule.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Green API webhooks reliably in minutes.