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-messagesInbound messages are returned newest first.
Request parameters
| Query parameter | Type | Required | Limitations | Details |
|---|---|---|---|---|
whatsapp_account_id | string | no | Must be one of your WhatsApp senders. | The MailerSend sender ID of the phone number that received the messages. See Sender identifiers. |
from | string | no | Max: 255 characters | The customer's phone number in international format, or their BSUID. See Filtering. |
type[] | string[] | no | Possible values: text, image, audio, video, document, sticker, location, contacts, interactive, button, order, reaction, system, unknown, unsupported | Array only. Multiple values are combined with OR. |
date_from | int|string | no | Timestamp 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_to | int|string | no | Timestamp 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. |
page | int | no | Min: 1, Max: 100 | To reach older messages beyond page 100, narrow date_from and date_to. |
limit | int | no | Min: 1, Max: 100 | Default: 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=50curl --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);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));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)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)
}
}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();
}
}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
)Responses
Valid
| Response key | Type | Details |
|---|---|---|
data[].id | string | ID of the inbound message. The same value is sent as data.whatsapp_inbound_message_id in the whatsapp.inbound webhook. |
data[].whatsapp_account_id | string | The MailerSend sender ID of the phone number that received the message. |
data[].from | string | The phone number of the person who sent the message, or their BSUID. Use it as to in POST /v1/whatsapp/send to reply. |
data[].to | string | Your phone number that received the message. An empty string for a sender connected with a Meta virtual number. |
data[].type | string | The kind of message. It determines the shape of meta. |
data[].received_at | string | When WhatsApp received the message, in ISO 8601 format. |
data[].meta | object|null | The content of the message. Its shape depends on type. See Message content. |
data[].context | object | Present only when the message is a reply. context.message_id is the WhatsApp ID of the message that was replied to. |
links.first | string | Full URL of the first page, with all your query parameters preserved. |
links.last | null | Always null. |
links.prev | string|null | Full URL of the previous page. null on the first page. |
links.next | string|null | Full URL of the next page. null on the last page. |
meta.current_page | int | The effective page. |
meta.current_page_url | string | Full URL of the current page. |
meta.from | int|null | Index of the first row on this page within the result set. null when the page is empty. |
meta.path | string | The URL of the endpoint, without query parameters. |
meta.per_page | int | The effective limit. |
meta.to | int|null | Index 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 EntityReturned 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 parameter | Type | Required | Limitations | Details |
|---|---|---|---|---|
inbound_message_id | string | yes | The 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');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));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)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)
}
}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();
}
}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')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 ForbiddenThe token is missing the whatsapp_read or whatsapp_full scope.
Response Code: 404 Not FoundReturned 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.
type | meta |
|---|---|
text | body with the message text. |
image, audio, video, document, sticker | The media file: url, status, mime_type, size, caption and more. See Media. |
location | latitude, longitude, name and address. |
reaction | message_id of the message that was reacted to, and emoji. |
button | text and payload of the quick reply button that was tapped. |
interactive | reply_type, id, title and description. reply_type is button_reply or list_reply. |
contacts | contacts, an array of the shared contact cards as sent by WhatsApp. |
order, system, unknown, unsupported | The 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"
}
}| Field | Type | Details |
|---|---|---|
meta.url | string|null | A signed, time-limited URL to download the file. It expires together with the stored file. null when the file is not stored. |
meta.status | string | stored 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_type | string | The media type reported by WhatsApp. Present when known. |
meta.size | integer | The file size in bytes. Present when known. |
meta.caption | string | The caption, when the sender added one. |
meta.filename | string | The original file name, when known. Usually present for documents. |
meta.animated | boolean | Whether 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.
Activities
Retrieve the status events of your WhatsApp messages with the MailerSend API. Get a list of activities filtered by message, item, recipient, type and date.
Recipients
Retrieve the WhatsApp recipients of your account with the MailerSend API. Get a list of recipients filtered by status or phone number, or a single recipient with their latest deliveries.