WhatsApp

Business AI agent

Set up Meta's AI agent on a WhatsApp number through the API, teach it about the business, test it, roll it out and hand chats back to people.

Meta offers its own AI agent for WhatsApp Business numbers. When it is on, it answers customers in the chat using the business information, FAQs, websites and files you give it, follows the tone and rules you write as skills, and can call your APIs to look things up. Pozy exposes the whole setup under /v1/accounts/{accountId}/business-agent, so you can configure the agent for a merchant without them opening Meta's tools, except for two steps Meta keeps for the merchant.

#What only the merchant can do

Step Where What happens until it is done
Accept Meta's Business Agent terms The Business Agent tab in WhatsApp Manager Every agent call returns business_agent_terms_not_accepted
Attach a payment method to the Business Agent billable account Meta's Billing Hub The agent can be switched on, but Meta delivers none of its replies to the public. Testing with an allowlist still works.

GET /v1/accounts/{accountId}/business-agent reports the first under manualSteps (with the URL to send the merchant to). Meta does not expose billing state, so the second is listed under unverifiedSteps once an agent exists and you decide when to stop showing it.

#Which numbers qualify

The number must be on the Cloud API, in a vertical and country Meta supports, in good standing, and not running another AI product. Meta allows one AI product per number, so a coexistence number that still has the WhatsApp Business app's own AI linked is not eligible until the merchant unlinks it in the app. eligible on the status read is Meta's answer, and creating an agent on an ineligible number returns 403 business_agent_not_eligible.

#Set it up

#Check where the merchant stands

cURL
curl https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent \
  -H "Authorization: Bearer $POZY_API_KEY"
Response
{
  "eligible": true,
  "termsAccepted": true,
  "onboarded": false,
  "enabled": false,
  "agentId": null,
  "settings": null,
  "manualSteps": [],
  "unverifiedSteps": []
}

#Create the agent

Call POST .../business-agent/onboard once. Meta then prepares the agent, which takes about a minute. Configuration calls made before it is ready return business_agent_not_found, so wait and retry.

#Give it knowledge

Everything the agent says comes from what you load here.

What Call Notes
Business information PUT .../business-information Payment methods, return policy, how to buy, shipping, description, email, hours and address. A full replace: fields you leave out are cleared.
FAQs POST .../faqs One specific question per entry (question up to 1,000 characters, answer up to 8,000). Answer quality drops past a few hundred entries.
Websites POST .../websites Meta crawls the site and recrawls it from time to time. Narrow it with included and excluded subdomains and URL patterns. Check crawl_status and crawl_error.
Files POST .../files PDF, DOC, DOCX, PNG and JPG (CSV and XLSX when Meta enables it for the account), up to 100 MB

None of these create calls are idempotent. A retry adds a second entry, so check the list before retrying after a timeout.

curl -X POST https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent/faqs \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Can I order a cake for the same day?",
    "answer": "Same-day cakes are available until 11am for pickup after 4pm. For delivery, order a day ahead."
  }'

#Give it a voice

  • Skills (POST .../skills) are written instructions in the brand's voice: how to greet, what to never promise, when to suggest a product. Each skill is up to 20,000 characters and reads pending_review until Meta's content review passes it.
  • UI skills (POST .../ui-skills) tell the agent when to send a rich component (a link button, image, carousel, list, reply buttons, location or a Flow) and what to put in it.
  • Never-say phrases live in the settings (never_say_phrases) and replace the whole list on each update.

#Connect your systems (optional)

A connector lets the agent call your API mid-conversation, for example to check an order status. Create it with a base_url and an auth_type (OAUTH2_CLIENT_CREDENTIALS, API_KEY or NONE) using POST .../connectors, then define each operation as a tool with POST .../connectors/{connectorId}/tools.

Run a tool once with .../run to check it before the agent relies on it. Rotate secrets with .../credentials. For an MCP server connector, .../refresh-tools rediscovers its tools. .../logs shows failures from the last 7 days.

#Test it privately

POST .../test-messages runs a message through the full agent in Meta's sandbox. No customer is involved and no tokens are billed. Pass the returned conversation_id back to continue the same thread. Meta limits how many test messages a number can send per hour.

cURL
curl -X POST https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent/test-messages \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you deliver to Cascais on Sundays?" }'

The response holds agent_response, plus handoff_reason or no_response_reason when the agent chose not to answer, and any quick_replies it offered.

For a broader check, POST .../evals simulates a set of scenarios and scores them. It answers 202 with a job id to poll with GET .../evals.

#Roll it out

Turn the agent on with PATCH .../settings. Start with a few real numbers on an allowlist, then open it to everyone.

cURL
# 1. Allow a test phone
curl -X POST https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent/allowlist \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "consumer_phone_number": "+351912345678" }'

# 2. Switch on for allowlisted people only
curl -X PATCH https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent/settings \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rollout": { "enabled": true },
    "ai_audience": "ALLOWLISTED_ONLY",
    "handoff": { "enabled": true, "message_selection": "CUSTOM", "message": "Let me get someone from the shop to help you." }
  }'

When you are happy, set ai_audience to EVERYONE. That needs the payment method from the merchant steps above.

#Settings

Setting Effect
rollout.enabled true turns the agent on for new conversations. false stops it on every thread.
ai_audience ALLOWLISTED_ONLY or EVERYONE
handoff What the agent says when it passes a chat to a person (DEFAULT, AGENT or CUSTOM message, up to 1,024 characters)
followup A nudge sent after a quiet period: 5 minutes to 24 hours, or 0 for off
never_say_phrases Phrases the agent must never use

Settings update partially: fields you leave out keep their value.

#Work alongside the agent

While the agent holds a conversation, Pozy still sees everything:

  • Customer messages arrive as message.received with metadata.standby: true.
  • The agent's replies arrive as message.sent with source: "meta_business_agent".

Sending any message from Pozy takes the conversation back from the agent. If you run your own bot on the same number, skip messages marked standby or you will interrupt the agent on every turn.

The agent also hands over by itself when it is unsure or the customer asks for a person. To give a conversation back to the agent without sending anything, call POST /v1/inbox/conversations/{conversationId}/thread-control with action: "release". Every change of control fires conversation.control_changed.

#Tell the agent about events

When something happens in your systems that the customer should hear about (an order shipped, a document approved), send it with POST .../business-agent/events: the customer's number in to, a type, a description and a payload. The agent writes the message. The customer must already have a conversation with the number. The call answers 202 with an event id; check the outcome with GET .../events/{eventId}.

#Costs

Meta bills agent replies by tokens on the merchant's Business Agent billable account, separately from Pozy. Meta's own figure is roughly 4 to 5 US cents per message. Cap the spend with PUT .../budget: each budget is a max_budget of token or ai_turn units over one_day, seven_days, fourteen_days or thirty_days. Sending the list replaces all budgets, and an empty list means no cap. When a cap is hit the agent finishes its turn, stops answering and hands the thread to a person until the window rolls over.

#Endpoints

Endpoint What it does
GET /v1/accounts/{accountId}/business-agent Get agent setup status
POST /v1/accounts/{accountId}/business-agent/onboard Create the agent
GET /v1/accounts/{accountId}/business-agent/settings List agent settings
PATCH /v1/accounts/{accountId}/business-agent/settings Update agent settings
GET /v1/accounts/{accountId}/business-agent/allowlist List allowlisted consumers
POST /v1/accounts/{accountId}/business-agent/allowlist Allowlist a consumer
DELETE /v1/accounts/{accountId}/business-agent/allowlist/{entryId} Remove an allowlisted consumer
GET /v1/accounts/{accountId}/business-agent/business-information Get business information
PUT /v1/accounts/{accountId}/business-agent/business-information Replace business information
DELETE /v1/accounts/{accountId}/business-agent/business-information Reset business information
GET /v1/accounts/{accountId}/business-agent/faqs List FAQs
POST /v1/accounts/{accountId}/business-agent/faqs Create a FAQ
GET /v1/accounts/{accountId}/business-agent/faqs/{faqId} Get a FAQ
PUT /v1/accounts/{accountId}/business-agent/faqs/{faqId} Update a FAQ
DELETE /v1/accounts/{accountId}/business-agent/faqs/{faqId} Delete a FAQ
GET /v1/accounts/{accountId}/business-agent/websites List crawled websites
POST /v1/accounts/{accountId}/business-agent/websites Add a website to crawl
GET /v1/accounts/{accountId}/business-agent/websites/{websiteId} Get a crawled website
PUT /v1/accounts/{accountId}/business-agent/websites/{websiteId} Update a crawled website
DELETE /v1/accounts/{accountId}/business-agent/websites/{websiteId} Remove a crawled website
GET /v1/accounts/{accountId}/business-agent/files List knowledge files
POST /v1/accounts/{accountId}/business-agent/files Upload a knowledge file
GET /v1/accounts/{accountId}/business-agent/files/{fileId} Get a knowledge file
DELETE /v1/accounts/{accountId}/business-agent/files/{fileId} Delete a knowledge file
GET /v1/accounts/{accountId}/business-agent/skills List skills
POST /v1/accounts/{accountId}/business-agent/skills Create a skill
GET /v1/accounts/{accountId}/business-agent/skills/{skillId} Get a skill
PUT /v1/accounts/{accountId}/business-agent/skills/{skillId} Update a skill
DELETE /v1/accounts/{accountId}/business-agent/skills/{skillId} Delete a skill
GET /v1/accounts/{accountId}/business-agent/ui-skills List UI skills
POST /v1/accounts/{accountId}/business-agent/ui-skills Create a UI skill
GET /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId} Get a UI skill
PUT /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId} Update a UI skill
DELETE /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId} Delete a UI skill
GET /v1/accounts/{accountId}/business-agent/connectors List connectors
POST /v1/accounts/{accountId}/business-agent/connectors Create a connector
GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId} Get a connector
PUT /v1/accounts/{accountId}/business-agent/connectors/{connectorId} Update a connector
DELETE /v1/accounts/{accountId}/business-agent/connectors/{connectorId} Delete a connector
POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/credentials Set connector credentials
POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/refresh-tools Refresh MCP connector tools
GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/logs Get connector failure logs
GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools List connector tools
POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools Create a connector tool
GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId} Get a connector tool
PUT /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId} Update a connector tool
DELETE /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId} Delete a connector tool
POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId}/run Run a connector tool once
GET /v1/accounts/{accountId}/business-agent/budget Get usage budgets
PUT /v1/accounts/{accountId}/business-agent/budget Replace usage budgets
POST /v1/accounts/{accountId}/business-agent/test-messages Send a test message
POST /v1/accounts/{accountId}/business-agent/events Send a business event
GET /v1/accounts/{accountId}/business-agent/events/{eventId} Get a business event status
GET /v1/accounts/{accountId}/business-agent/evals Read evaluation data
POST /v1/accounts/{accountId}/business-agent/evals Start an evaluation run

#Webhook events

Event When it fires
message.received Message received.
conversation.control_changed Conversation control changed.
message.sent Message sent.