MailerSend
WhatsApp API

Inbound messages

Retrieve the WhatsApp messages your customers sent to your phone numbers with the MailerSend API. Filter by phone number, customer, message type and date.

Inbound messages are the messages people send to your WhatsApp phone numbers, including replies to your template messages and button taps. They are also delivered in real time as whatsapp.inbound webhook events.

The API returns the content of every message type in a single meta object, while the webhook payload carries it in a field named after the type. See Message content.

Required token scopes

Requires a token with one of the whatsapp_read or whatsapp_full scopes.

Data retention

Inbound messages are kept for the inbound retention period of your plan: 7, 14 or 30 days. An inbound message older than that is no longer listed, and requesting it returns 404.

Get a list of inbound messages

Get a list of the WhatsApp messages received by your phone numbers with this GET request:

GET https://api.mailersend.com/v1/whatsapp/inbound-messages

Inbound messages are returned newest first.

Request parameters

Query parameterTypeRequiredLimitationsDetails
whatsapp_account_idstringnoMust be one of your WhatsApp senders.The MailerSend sender ID of the phone number that received the messages. See Sender identifiers.
fromstringnoMax: 255 charactersThe customer's phone number in international format, or their BSUID. See Filtering.
type[]string[]noPossible values: text, image, audio, video, document, sticker, location, contacts, interactive, button, order, reaction, system, unknown, unsupportedArray only. Multiple values are combined with OR.
date_fromint|stringnoTimestamp is assumed to be UTC. Must be lower than date_to. No fractional seconds: 2026-10-14T00:00:00.000Z is rejected.Unix timestamp: 1791936000, ISO 8601 datetime: 2026-10-14T00:00:00Z or datetime: 2026-10-14 00:00:00. Exclusive. Applied to received_at.
date_toint|stringnoTimestamp is assumed to be UTC. Must be higher than date_from. No fractional seconds: 2026-10-15T00:00:00.000Z is rejected.Unix timestamp: 1792022400, ISO 8601 datetime: 2026-10-15T00:00:00Z or datetime: 2026-10-15 00:00:00. Exclusive. Applied to received_at.
pageintnoMin: 1, Max: 100To reach older messages beyond page 100, narrow date_from and date_to.
limitintnoMin: 1, Max: 100Default: 25

Filtering

date_from and date_to are exclusive: only messages received strictly after date_from and strictly before date_to are returned. Both accept a Unix timestamp, an ISO 8601 datetime with Z or an offset such as 2026-10-14T00:00:00+02:00, or a datetime in Y-m-d H:i:s format. A date without a time, such as 2026-10-14, returns a 422 validation error.

from accepts a phone number in international format, or a BSUID for a customer whose phone number WhatsApp doesn't share. In phone numbers, spaces, dashes and the leading + are ignored, so +48 600-000-001 and 48600000001 match the same customer. A WhatsApp username is not accepted.

type[] must be sent as an array, for example ?type[]=text&type[]=image.

An example request that returns the text and image messages one phone number received over one day:

GET https://api.mailersend.com/v1/whatsapp/inbound-messages?whatsapp_account_id=3enl6x27wmrxrl2v&type[]=text&type[]=image&date_from=2026-10-14T00:00:00Z&date_to=2026-10-15T00:00:00Z&limit=50
curl --request GET \
  --url 'https://api.mailersend.com/v1/whatsapp/inbound-messages?whatsapp_account_id=3enl6x27wmrxrl2v&from=48600000001&type[]=text&type[]=image&date_from=2026-10-14T00:00:00Z&date_to=2026-10-15T00:00:00Z&limit=25' \
  --globoff \
  --header 'Authorization: Bearer API_KEY_HERE'
use MailerSend\MailerSend;
use MailerSend\Helpers\Builder\WhatsappInboundMessageParams;

$mailersend = new MailerSend(['api_key' => 'key']);

$whatsappInboundMessageParams = (new WhatsappInboundMessageParams())
    ->setWhatsappAccountId('whatsapp_account_id')
    ->setFrom('48600000001')
    ->setType(['text', 'image'])
    ->setDateFrom(1790000000)
    ->setDateTo(1790086400)
    ->setPage(1)
    ->setLimit(25);

$whatsappInboundMessages = $mailersend->whatsappInboundMessage->getAll($whatsappInboundMessageParams);

More examples

import 'dotenv/config';
import { MailerSend } from "mailersend";

const mailerSend = new MailerSend({
  apiKey: process.env.API_KEY,
});

mailerSend.whatsapp.inboundMessage.list({
  whatsapp_account_id: "whatsapp_account_id",
  from: "48600000001",
  type: ["text", "image"],
  date_from: 1790000000,
  date_to: 1790086400,
  limit: 25,
  page: 1,
})
  .then((response) => console.log(response.body))
  .catch((error) => console.log(error.body));

More examples

from mailersend import MailerSendClient, WhatsAppInboundMessagesBuilder
from datetime import datetime, timedelta

ms = MailerSendClient()

# Get inbound messages from the last day
date_from = int((datetime.now() - timedelta(days=1)).timestamp())
date_to = int(datetime.now().timestamp())

request = (WhatsAppInboundMessagesBuilder()
          .whatsapp_account_id("whatsapp-account-id")
          .from_number("48600000001")
          .type(["text", "image"])
          .date_from(date_from)
          .date_to(date_to)
          .page(1)
          .limit(25)
          .build_list_request())

response = ms.whatsapp_inbound_messages.list_whatsapp_inbound_messages(request)

More examples

package main

import (
	"context"
	"log"
	"time"

	"github.com/mailersend/mailersend-go"
)

var APIKey = "Api Key Here"

func main() {
	// Create an instance of the mailersend client
	ms := mailersend.NewMailersend(APIKey)

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	from := time.Now().Add(-24 * time.Hour).Unix()
	to := time.Now().Unix()

	options := &mailersend.ListWhatsAppInboundMessageOptions{
		WhatsAppAccountID: "whatsapp-account-id",
		From:              "48600000001",
		Type:              []string{"text", "image"},
		DateFrom:          from,
		DateTo:            to,
		Page:              1,
		Limit:             25,
	}

	_, _, err := ms.WhatsAppInboundMessage.List(ctx, options)
	if err != nil {
		log.Fatal(err)
	}
}

More examples

import com.mailersend.sdk.MailerSend;
import com.mailersend.sdk.exceptions.MailerSendException;
import com.mailersend.sdk.whatsapp.inboundmessages.WhatsAppInboundMessage;
import com.mailersend.sdk.whatsapp.inboundmessages.WhatsAppInboundMessageList;

public void getWhatsAppInboundMessages() {

    MailerSend ms = new MailerSend();
    ms.setToken("mailersend token");

    try {

        WhatsAppInboundMessageList list = ms.whatsapp().inboundMessages()
            .whatsappAccountId("whatsapp account id")
            .from("48600000001")
            .types(new String[]{"text", "image"})
            .dateFrom(1790000000)
            .dateTo(1790086400)
            .page(1)
            .limit(25)
            .getInboundMessages();

        for (WhatsAppInboundMessage inboundMessage : list.inboundMessages) {
            System.out.println(inboundMessage.id);
            System.out.println(inboundMessage.from);
            System.out.println(inboundMessage.type);
        }

    } catch (MailerSendException e) {

        e.printStackTrace();
    }
}

More examples

require "mailersend-ruby"

# Intialize the WhatsApp Inbound Messages class
ms_whatsapp_inbound_messages = Mailersend::WhatsAppInboundMessages.new

# Add parameters
ms_whatsapp_inbound_messages.list(
  whatsapp_account_id: 'your-whatsapp-account-id',
  from: '48600000001',
  type: %w[text image],
  date_from: 1790000000,
  date_to: 1790086400,
  page: 1,
  limit: 25
)

More examples

Responses

Valid

Response keyTypeDetails
data[].idstringID of the inbound message. The same value is sent as data.whatsapp_inbound_message_id in the whatsapp.inbound webhook.
data[].whatsapp_account_idstringThe MailerSend sender ID of the phone number that received the message.
data[].fromstringThe phone number of the person who sent the message, or their BSUID. Use it as to in POST /v1/whatsapp/send to reply.
data[].tostringYour phone number that received the message. An empty string for a sender connected with a Meta virtual number.
data[].typestringThe kind of message. It determines the shape of meta.
data[].received_atstringWhen WhatsApp received the message, in ISO 8601 format.
data[].metaobject|nullThe content of the message. Its shape depends on type. See Message content.
data[].contextobjectPresent only when the message is a reply. context.message_id is the WhatsApp ID of the message that was replied to.
links.firststringFull URL of the first page, with all your query parameters preserved.
links.lastnullAlways null.
links.prevstring|nullFull URL of the previous page. null on the first page.
links.nextstring|nullFull URL of the next page. null on the last page.
meta.current_pageintThe effective page.
meta.current_page_urlstringFull URL of the current page.
meta.fromint|nullIndex of the first row on this page within the result set. null when the page is empty.
meta.pathstringThe URL of the endpoint, without query parameters.
meta.per_pageintThe effective limit.
meta.toint|nullIndex of the last row on this page within the result set. null when the page is empty.
Response Code: 200 OK
Response Headers:
	content-type: application/json
{
  "data": [
    {
      "id": "62f114f7165fe0d8db0288e2",
      "whatsapp_account_id": "3enl6x27wmrxrl2v",
      "from": "48600000001",
      "to": "15550001234",
      "type": "text",
      "received_at": "2026-10-14T12:41:19.000000Z",
      "meta": {
        "body": "Hello, I would like to check my order status."
      },
      "context": {
        "message_id": "wamid.HBgLMzcwNjMzMjI3OTcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA"
      }
    },
    {
      "id": "62f114c1165fe0d8db0288d9",
      "whatsapp_account_id": "3enl6x27wmrxrl2v",
      "from": "US.13491208655302741918",
      "to": "15550001234",
      "type": "image",
      "received_at": "2026-10-14T12:38:03.000000Z",
      "meta": {
        "url": "https://app.mailersend.com/whatsapp/inbound-message-attachment/62f114c1165fe0d8db0288d9?account=3enl6x27wmrxrl2v&expires=1792022400&signature=...",
        "status": "stored",
        "mime_type": "image/jpeg",
        "size": 102400,
        "caption": "Here is the damaged item"
      }
    }
  ],
  "links": {
    "first": "https:\/\/api.mailersend.com\/v1\/whatsapp\/inbound-messages?page=1",
    "last": null,
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "current_page_url": "https:\/\/api.mailersend.com\/v1\/whatsapp\/inbound-messages?page=1",
    "from": 1,
    "path": "https:\/\/api.mailersend.com\/v1\/whatsapp\/inbound-messages",
    "per_page": 25,
    "to": 2
  }
}

The top-level meta object holds the pagination details, while data[].meta holds the content of each message.

Paginating through the results

The list uses simple pagination. The response carries no total and no last_page, and links.last is always null. Follow the URL in links.next, which keeps the filters you sent, until it is null.

Error

Response Code: 403 Forbidden
{
  "message": "The custom API token you're using doesn't have the required permissions. #MS40301"
}

The token is missing the whatsapp_read or whatsapp_full scope.

Response Code: 422 Unprocessable Entity

Returned when a parameter is invalid — including a whatsapp_account_id that does not belong to your account, a from that is longer than 255 characters or is not a phone number or BSUID, such as a WhatsApp username, an unknown value in type[], a date_from or date_to without a time, a date_to that is not after date_from, or a page or limit out of range.

See - Validation errors

Get an inbound message

Get a single inbound message with this GET request:

GET https://api.mailersend.com/v1/whatsapp/inbound-messages/{inbound_message_id}

Request parameters

URL parameterTypeRequiredLimitationsDetails
inbound_message_idstringyesThe id of an inbound message, as returned by GET /v1/whatsapp/inbound-messages or sent as data.id in the whatsapp.inbound webhook.
curl --request GET \
  --url https://api.mailersend.com/v1/whatsapp/inbound-messages/62f114a3165fe0d8db0288d1 \
  --header 'Authorization: Bearer API_KEY_HERE'
use MailerSend\MailerSend;

$mailersend = new MailerSend(['api_key' => 'key']);

$whatsappInboundMessage = $mailersend->whatsappInboundMessage->find('inbound_message_id');

More examples

import 'dotenv/config';
import { MailerSend } from "mailersend";

const mailerSend = new MailerSend({
  apiKey: process.env.API_KEY,
});

mailerSend.whatsapp.inboundMessage.single("inbound_message_id")
  .then((response) => console.log(response.body))
  .catch((error) => console.log(error.body));

More examples

from mailersend import MailerSendClient, WhatsAppInboundMessagesBuilder

ms = MailerSendClient()

request = (WhatsAppInboundMessagesBuilder()
          .whatsapp_inbound_message_id("inbound-message-id")
          .build_get_request())

response = ms.whatsapp_inbound_messages.get_whatsapp_inbound_message(request)

More examples

package main

import (
	"context"
	"log"
	"time"

	"github.com/mailersend/mailersend-go"
)

var APIKey = "Api Key Here"

func main() {
	// Create an instance of the mailersend client
	ms := mailersend.NewMailersend(APIKey)

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	_, _, err := ms.WhatsAppInboundMessage.Get(ctx, "inbound-message-id")
	if err != nil {
		log.Fatal(err)
	}
}

More examples

import com.mailersend.sdk.MailerSend;
import com.mailersend.sdk.exceptions.MailerSendException;
import com.mailersend.sdk.whatsapp.inboundmessages.WhatsAppInboundMessage;

public void getWhatsAppInboundMessage() {

    MailerSend ms = new MailerSend();
    ms.setToken("mailersend token");

    try {

        WhatsAppInboundMessage inboundMessage = ms.whatsapp().inboundMessages().getInboundMessage("inbound message id");

        System.out.println(inboundMessage.id);
        System.out.println(inboundMessage.type);

    } catch (MailerSendException e) {

        e.printStackTrace();
    }
}

More examples

require "mailersend-ruby"

# Intialize the WhatsApp Inbound Messages class
ms_whatsapp_inbound_messages = Mailersend::WhatsAppInboundMessages.new

# Add parameters
ms_whatsapp_inbound_messages.get(whatsapp_inbound_message_id: 'your-whatsapp-inbound-message-id')

More examples

Responses

Valid

The response contains the same fields as an item of the list.

Response Code: 200 OK
Response Headers:
	content-type: application/json
{
  "data": {
    "id": "62f114a3165fe0d8db0288d1",
    "whatsapp_account_id": "3enl6x27wmrxrl2v",
    "from": "48600000001",
    "to": "15550001234",
    "type": "interactive",
    "received_at": "2026-10-14T12:36:44.000000Z",
    "meta": {
      "reply_type": "list_reply",
      "id": "delivery_today",
      "title": "Deliver today",
      "description": "Between 4 PM and 8 PM"
    },
    "context": {
      "message_id": "wamid.HBgLMzcwNjMzMjI3OTcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA"
    }
  }
}

Error

Response Code: 403 Forbidden

The token is missing the whatsapp_read or whatsapp_full scope.

Response Code: 404 Not Found

Returned when the inbound message does not exist, belongs to another account, or is older than your plan's inbound retention period.

Message content

The content of an inbound message is returned in meta, and its shape depends on the message type.

typemeta
textbody with the message text.
image, audio, video, document, stickerThe media file: url, status, mime_type, size, caption and more. See Media.
locationlatitude, longitude, name and address.
reactionmessage_id of the message that was reacted to, and emoji.
buttontext and payload of the quick reply button that was tapped.
interactivereply_type, id, title and description. reply_type is button_reply or list_reply.
contactscontacts, an array of the shared contact cards as sent by WhatsApp.
order, system, unknown, unsupportedThe object WhatsApp sent under the field named after the type, as received. null when WhatsApp sent none.

Fields that WhatsApp did not send, such as the name of a shared location or the caption of an image, can be null or missing, so check that a field is set before you use it.

Some examples of meta:

{
  "type": "location",
  "meta": {
    "latitude": 52.2297,
    "longitude": 21.0122,
    "name": "Shop Brand Warehouse",
    "address": "Prosta 20, 00-850 Warsaw"
  }
}
{
  "type": "reaction",
  "meta": {
    "message_id": "wamid.HBgLMzcwNjMzMjI3OTcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA",
    "emoji": "\ud83d\udc4d"
  }
}
{
  "type": "button",
  "meta": {
    "text": "Track my order",
    "payload": "track_order_12345"
  }
}

Media

For media messages, meta describes the file MailerSend downloaded from WhatsApp and links to it.

{
  "type": "document",
  "meta": {
    "url": "https://app.mailersend.com/whatsapp/inbound-message-attachment/62f114c1165fe0d8db0288d9?account=3enl6x27wmrxrl2v&expires=1792022400&signature=...",
    "status": "stored",
    "mime_type": "application/pdf",
    "size": 48213,
    "filename": "invoice-12345.pdf"
  }
}
FieldTypeDetails
meta.urlstring|nullA signed, time-limited URL to download the file. It expires together with the stored file. null when the file is not stored.
meta.statusstringstored when the file is available, pending while it is being downloaded, failed when it could not be downloaded, and expired when it is no longer kept.
meta.mime_typestringThe media type reported by WhatsApp. Present when known.
meta.sizeintegerThe file size in bytes. Present when known.
meta.captionstringThe caption, when the sender added one.
meta.filenamestringThe original file name, when known. Usually present for documents.
meta.animatedbooleanWhether the sticker is animated. Present for stickers only.

Media expires

Inbound media is kept for 1 day, which is shorter than the inbound message itself. Once the file has expired, meta.status is expired and meta.url is null. Download the file as soon as you can if you need to keep it.

On this page