How to Reply to WhatsApp Business Messages with an AI Agent
Connect the Postproxy MCP server and let Claude, ChatGPT, or Cursor work your WhatsApp Business inbox — reading conversations, replying, and sending approved templates when the 24-hour window has closed.
What this gets you
An agent that works a WhatsApp Business inbox: it reads the open conversations, drafts replies in your voice, and respects the one rule that makes WhatsApp different from every other channel — after 24 hours of silence, a free-form message is rejected and only an approved template will go through.
Everything below happens through MCP tools. There is no integration to write, no endpoint to call, and no API key to pass around in code — you connect the server once and then work in prompts.
1. Connect the MCP server
Full setup instructions, including the hosted and local options and the one-click Claude and ChatGPT connectors, are on the MCP integration page. The short version, hosted with nothing to install:
claude mcp add --transport http postproxy \ https://mcp.postproxy.dev/mcp?api_key=YOUR_POSTPROXY_API_KEYOnce it’s connected, your client discovers the tools and you can check it’s alive by asking:
“What Postproxy profiles do I have?”
That runs profiles_list. If you get a list back, you’re done with setup.
Develop against the sandbox first. Use a sandbox (t_…) API key and a sandbox WhatsApp profile ships with a fabricated number and two approved seed templates — hello_world and order_update — so the agent can run the whole loop before Meta has reviewed anything of yours. Inbound messages are driven from the Sandbox page in the dashboard.
2. The tools your agent needs
| Tool | What it does |
|---|---|
profiles_list | Find the WhatsApp profile |
profiles_placements | List the phone numbers on it |
dm_chats_list | List conversations, most recent first, with the messaging-window flag |
dm_messages_list | Read a thread |
dm_message_send | Reply — text, media, a template, or interactive buttons |
dm_chat_create | Open a conversation with a phone number (needs placement_id) |
dm_chat_mark_read | Send WhatsApp’s blue ticks |
dm_message_react | React to a customer’s message |
whatsapp_templates_list | List approved templates and their variable counts |
The same dm_* tools already answer Instagram and Messenger, so if you’ve followed letting agents answer DMs and comments, WhatsApp slots into the workflow you have.
3. Connect a WhatsApp Business Account
The agent can start this too, with profile_groups_initialize_connection:
“Start a WhatsApp connection for my Main Brand profile group. The number is currently in the WhatsApp Business app on my phone.”
That last sentence matters — it selects the onboarding mode:
| Mode | When | What happens |
|---|---|---|
business_app | The number is in the WhatsApp Business app and your team answers from it | Coexistence. You scan a QR code, the phone keeps working, the agent sees the same conversations, and up to 6 months of history is imported |
api | The number is already on the Cloud API, or is a fresh number | The number moves to the Cloud API and stops working in the app |
| (omitted) | You’re not sure | The connect page asks before the Facebook login |
The tool returns a URL. Open it, complete Meta’s Embedded Signup, and the account is connected — one profile, with each of its phone numbers as a placement.
Coexistence is usually the right first move: you can put an agent on a live support line without cutting the line over. The trade-offs are Meta’s — lower sending throughput, no group chats through the API, and Meta disconnects a coexistence number whose phone stays offline for around 14 days.
4. Find the profile and its numbers
“List my profiles, then show me the phone numbers on the WhatsApp one.”
profiles_list finds the profile; profiles_placements lists the numbers. Each placement is one phone number, and its id is the placement_id the agent passes to dm_chat_create — required on WhatsApp, because a conversation belongs to a specific number. A customer who writes to two of your numbers has two chats.
The placement also carries the number’s quality_rating and messaging_limit_tier — Meta’s cap on how many conversations that number may start per 24 hours — which is worth glancing at before you point an automation at it.
One rule to hand the agent along with dm_chat_create: a brand-new conversation can only be opened with an approved template. There is no open window yet, so a plain-text first message is rejected. Reaching out cold and replying to someone who just wrote in are different jobs, and only the second one starts with text.
5. Read the inbox
dm_chats_list returns conversations newest-first. A WhatsApp chat comes back like this:
{ "id": "chat_wa789", "platform": "whatsapp", "external_placement_id": "106540352242922", "participant_external_id": "13105550007", "participant_name": "Ana", "group": false, "within_messaging_window": true, "last_inbound_at": "2026-09-28T08:12:00.000Z", "last_outbound_at": "2026-09-27T16:40:00.000Z"}Two fields drive every decision the agent makes:
within_messaging_window— whether a free-form reply is allowed right now.last_inbound_atvslast_outbound_at— whether the customer is still waiting on you.
6. Teach it the window rule
This is the step that decides whether the agent is useful or embarrassing. Meta allows free-form messages only within 24 hours of the customer’s last message. Outside that, a plain text send is rejected — and it’s rejected by Meta after the send is accepted, so the agent can’t learn from a failed call and try again.
within_messaging_window is the pre-flight check. Put the rule in your system prompt rather than hoping the model infers it:
When working WhatsApp chats, always check
within_messaging_windowfirst.If it is
true, reply in plain text withdm_message_send.If it is
false, do not send plain text. Callwhatsapp_templates_listand send an approved template instead, filling its variables from the conversation. If no template fits the situation, stop and tell me — do not improvise.
7. Reply, or send a template
Inside the window, the agent calls dm_message_send with plain text:
{ "chat_id": "chat_wa789", "body": "Setting one aside now — it will be at the counter until Friday."}Outside it, the same tool takes a template instead of body:
{ "chat_id": "chat_wa789", "template": { "name": "order_update", "language": "en_US", "variables": ["Ana", "ORD-12345", "Friday"] }}variables are positional — header variables first, then body, then one per dynamic URL button. Each template from whatsapp_templates_list reports a variable_count, and a wrong count comes back as a clear error naming the expected number, so the agent gets something it can correct rather than a silent failure.
Give the agent the template list up front, with a sentence about what each one is for. That’s the difference between a working fallback and an agent that reports it couldn’t reply:
“List the approved WhatsApp templates and tell me which ones you’d use for a delayed order, a ready-for-pickup notice, and an abandoned enquiry.”
A template send also re-opens the 24-hour window, so once the customer replies the agent is back to normal text.
8. Ask with buttons instead of open questions
When the next step is a choice, buttons beat free text — the customer taps, and the agent gets a stable identifier back instead of parsing a sentence. dm_message_send takes WhatsApp’s interactive object:
{ "chat_id": "chat_wa789", "interactive": { "type": "button", "body": { "text": "Your order is ready. Pick up or deliver?" }, "action": { "buttons": [ { "type": "reply", "reply": { "id": "PICKUP", "title": "Pick up" } }, { "type": "reply", "reply": { "id": "DELIVER", "title": "Deliver" } } ] } }}The tap arrives as an inbound message whose interactive carries the id you assigned. Have the agent branch on that id, not on the message text — otherwise the flow breaks the first time somebody translates a label. Lists (up to 10 rows), call-to-action URLs, catalog products, and Flows use the same envelope.
Interactive messages are session messages: they need an open window, and they can’t go to a group.
9. A session end to end
With the server connected, the whole job is prompting:
“Check the WhatsApp inbox. For every conversation where the customer wrote last and we haven’t replied, draft a response. Show me the drafts before sending anything.”
The agent calls dm_chats_list, compares last_inbound_at against last_outbound_at, reads each thread with dm_messages_list, and drafts. Nothing leaves until you approve.
“For the ones where the window has closed, use the order_update template with the right order number.”
It reads within_messaging_window, pulls the template from whatsapp_templates_list, and sends with template and variables taken from the thread.
“Mark everything you answered as read.”
dm_chat_mark_read sends WhatsApp’s blue ticks, so the customer can see their message was actually seen rather than just receiving a reply out of nowhere.
“Anything that mentions a refund, leave alone and list it for me.”
The useful half of an agent inbox is what it declines to touch.
10. Running it unattended
For a human-in-the-loop workflow, the prompts above are enough. If you want the agent reacting on its own, two things change.
Something has to wake it. MCP tools are pull-only — the agent acts when you or your scheduler asks it to. Either run the loop on a schedule (“every 10 minutes, check for unanswered WhatsApp chats”), or have your own code subscribe to the message.received webhook and trigger the agent per message. The WhatsApp reference lists the events, including message_template.status_updated — worth watching, because Meta can pause a template after approving it, and a paused template is exactly the fallback your unattended loop depends on.
Guardrails stop being optional. WhatsApp is someone’s personal messaging app, and Meta enforces accordingly:
- Keep draft-then-approve while you’re tuning. One round trip, and it catches the replies you’d rather not have sent.
- Never let the agent talk its way past the window. The only legitimate routes are an approved template or a genuine transactional notification. Marketing content dressed as a utility message is a policy problem, not a clever workaround.
- Give it an explicit escape. Refunds, complaints, anything it isn’t sure about — flag for a human instead of answering.
- Watch the number’s quality rating. It’s what Meta uses to decide how many conversations you can start per day, and a slide toward
REDis the clearest signal that automated replies are landing badly.
Where to go next
- MCP integration — every setup path, the full tool list, and the one-click connectors.
- WhatsApp API reference — templates, number administration, and webhook payloads, if you outgrow tools and want the endpoints.
- WhatsApp notes in the Direct Messages API — exactly where WhatsApp differs from the other DM networks.
- Let AI agents answer DMs and comments via MCP — the same pattern across Instagram, Messenger, comments, and Google reviews.
- WhatsApp Business messaging in the same API as your social DMs — why WhatsApp is usually a separate stack, and what changes when it isn’t.