Gareth Wilson Gareth Wilson

Guide to Green API Webhooks: Features and Best Practices

Published


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

FeatureDetails
ConfigurationThe SetSettings method, or the instance's settings in the console
URLOne webhookUrl per instance
Subscription modelPer-type switches such as incomingWebhook, outgoingWebhook and stateWebhook
Method and formatPOST, Content-Type: application/json; charset=utf-8
Event identifiertypeWebhook in the body
SignatureNone
AuthenticationwebhookUrlToken, sent in the Authorization header
RetriesEvery minute until delivered
Delivery guaranteeWithin 24 hours
ThroughputAbout 50 notifications a second
Source IPs13 published addresses
AlternativePolling with ReceiveNotification and DeleteNotification

Common events

The body's typeWebhook field names the webhook type:

typeWebhookSent when
incomingMessageReceivedA message or file arrives
outgoingMessageReceivedA message is sent from the phone
outgoingAPIMessageReceivedA message is sent through the API
outgoingMessageStatusAn outgoing message's status changes
incomingMessageStatusAn incoming message's status changes
stateInstanceChangedThe instance's authorisation state changes
incomingCallA call comes in
outgoingCallA call is made
quotaExceededDeveloper plan limits are exceeded
statusInstanceChangedThe instance's socket state changes (archived)
deviceInfoThe phone's device and battery level are reported (archived)
incomingBlockA 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:

{
  "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:

{
  "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:

{
  "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:

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:

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 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 and our guide to 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 checks each Green API notification, stores it, and retries into your service on your schedule.

Get started with Hookdeck for free and handle Green API webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.