# 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`](https://pozy.ai/docs/api/getBusinessAgentStatus) 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

```bash cURL
curl https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent \
  -H "Authorization: Bearer $POZY_API_KEY"
```

```json
{
  "eligible": true,
  "termsAccepted": true,
  "onboarded": false,
  "enabled": false,
  "agentId": null,
  "settings": null,
  "manualSteps": [],
  "unverifiedSteps": []
}
```

### Create the agent

Call [`POST .../business-agent/onboard`](https://pozy.ai/docs/api/onboardBusinessAgent) 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`](https://pozy.ai/docs/api/replaceBusinessAgentBusinessInformation) | 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`](https://pozy.ai/docs/api/createBusinessAgentFaq) | 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`](https://pozy.ai/docs/api/addBusinessAgentWebsite) | 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`](https://pozy.ai/docs/api/uploadBusinessAgentFile) | 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.

```bash cURL
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."
  }'
```
```ts Node
const accountId = "6712a9d4c0e3b58f1a7d2e64";

const res = await fetch(`https://api.pozy.ai/v1/accounts/${accountId}/business-agent/faqs`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    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.",
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
```
```python Python
import os
import requests

H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
base = "https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/business-agent"

requests.post(f"{base}/faqs", headers=H, timeout=30, json={
    "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.",
}).raise_for_status()
```

### Give it a voice

- **Skills** ([`POST .../skills`](https://pozy.ai/docs/api/createBusinessAgentSkill)) 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`](https://pozy.ai/docs/api/createBusinessAgentUiSkill)) tell the agent when to send a rich component (a link button, image, carousel, list, reply buttons, location or a [Flow](https://pozy.ai/docs/whatsapp/flows)) 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`](https://pozy.ai/docs/api/createBusinessAgentConnector), then define each operation as a tool with [`POST .../connectors/{connectorId}/tools`](https://pozy.ai/docs/api/createBusinessAgentConnectorTool).

Run a tool once with [`.../run`](https://pozy.ai/docs/api/runBusinessAgentConnectorTool) to check it before the agent relies on it. Rotate secrets with [`.../credentials`](https://pozy.ai/docs/api/setBusinessAgentConnectorCredentials). For an MCP server connector, [`.../refresh-tools`](https://pozy.ai/docs/api/refreshBusinessAgentConnectorTools) rediscovers its tools. [`.../logs`](https://pozy.ai/docs/api/getBusinessAgentConnectorLogs) shows failures from the last 7 days.

### Test it privately

[`POST .../test-messages`](https://pozy.ai/docs/api/sendBusinessAgentTestMessage) 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.

```bash 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`](https://pozy.ai/docs/api/startBusinessAgentEvalRun) simulates a set of scenarios and scores them. It answers `202` with a job id to poll with [`GET .../evals`](https://pozy.ai/docs/api/readBusinessAgentEvals).

### Roll it out

Turn the agent on with [`PATCH .../settings`](https://pozy.ai/docs/api/updateBusinessAgentSettings). Start with a few real numbers on an allowlist, then open it to everyone.

```bash 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`](https://pozy.ai/docs/api/setConversationThreadControl) 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`](https://pozy.ai/docs/api/sendBusinessAgentEvent): 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}`](https://pozy.ai/docs/api/getBusinessAgentEvent).

## 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`](https://pozy.ai/docs/api/replaceBusinessAgentBudget): 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`](https://pozy.ai/docs/api/getBusinessAgentStatus) | Get agent setup status |
| [`POST /v1/accounts/{accountId}/business-agent/onboard`](https://pozy.ai/docs/api/onboardBusinessAgent) | Create the agent |
| [`GET /v1/accounts/{accountId}/business-agent/settings`](https://pozy.ai/docs/api/listBusinessAgentSettings) | List agent settings |
| [`PATCH /v1/accounts/{accountId}/business-agent/settings`](https://pozy.ai/docs/api/updateBusinessAgentSettings) | Update agent settings |
| [`GET /v1/accounts/{accountId}/business-agent/allowlist`](https://pozy.ai/docs/api/listBusinessAgentAllowlist) | List allowlisted consumers |
| [`POST /v1/accounts/{accountId}/business-agent/allowlist`](https://pozy.ai/docs/api/addBusinessAgentAllowlistEntry) | Allowlist a consumer |
| [`DELETE /v1/accounts/{accountId}/business-agent/allowlist/{entryId}`](https://pozy.ai/docs/api/removeBusinessAgentAllowlistEntry) | Remove an allowlisted consumer |
| [`GET /v1/accounts/{accountId}/business-agent/business-information`](https://pozy.ai/docs/api/getBusinessAgentBusinessInformation) | Get business information |
| [`PUT /v1/accounts/{accountId}/business-agent/business-information`](https://pozy.ai/docs/api/replaceBusinessAgentBusinessInformation) | Replace business information |
| [`DELETE /v1/accounts/{accountId}/business-agent/business-information`](https://pozy.ai/docs/api/resetBusinessAgentBusinessInformation) | Reset business information |
| [`GET /v1/accounts/{accountId}/business-agent/faqs`](https://pozy.ai/docs/api/listBusinessAgentFaqs) | List FAQs |
| [`POST /v1/accounts/{accountId}/business-agent/faqs`](https://pozy.ai/docs/api/createBusinessAgentFaq) | Create a FAQ |
| [`GET /v1/accounts/{accountId}/business-agent/faqs/{faqId}`](https://pozy.ai/docs/api/getBusinessAgentFaq) | Get a FAQ |
| [`PUT /v1/accounts/{accountId}/business-agent/faqs/{faqId}`](https://pozy.ai/docs/api/updateBusinessAgentFaq) | Update a FAQ |
| [`DELETE /v1/accounts/{accountId}/business-agent/faqs/{faqId}`](https://pozy.ai/docs/api/deleteBusinessAgentFaq) | Delete a FAQ |
| [`GET /v1/accounts/{accountId}/business-agent/websites`](https://pozy.ai/docs/api/listBusinessAgentWebsites) | List crawled websites |
| [`POST /v1/accounts/{accountId}/business-agent/websites`](https://pozy.ai/docs/api/addBusinessAgentWebsite) | Add a website to crawl |
| [`GET /v1/accounts/{accountId}/business-agent/websites/{websiteId}`](https://pozy.ai/docs/api/getBusinessAgentWebsite) | Get a crawled website |
| [`PUT /v1/accounts/{accountId}/business-agent/websites/{websiteId}`](https://pozy.ai/docs/api/updateBusinessAgentWebsite) | Update a crawled website |
| [`DELETE /v1/accounts/{accountId}/business-agent/websites/{websiteId}`](https://pozy.ai/docs/api/deleteBusinessAgentWebsite) | Remove a crawled website |
| [`GET /v1/accounts/{accountId}/business-agent/files`](https://pozy.ai/docs/api/listBusinessAgentFiles) | List knowledge files |
| [`POST /v1/accounts/{accountId}/business-agent/files`](https://pozy.ai/docs/api/uploadBusinessAgentFile) | Upload a knowledge file |
| [`GET /v1/accounts/{accountId}/business-agent/files/{fileId}`](https://pozy.ai/docs/api/getBusinessAgentFile) | Get a knowledge file |
| [`DELETE /v1/accounts/{accountId}/business-agent/files/{fileId}`](https://pozy.ai/docs/api/deleteBusinessAgentFile) | Delete a knowledge file |
| [`GET /v1/accounts/{accountId}/business-agent/skills`](https://pozy.ai/docs/api/listBusinessAgentSkills) | List skills |
| [`POST /v1/accounts/{accountId}/business-agent/skills`](https://pozy.ai/docs/api/createBusinessAgentSkill) | Create a skill |
| [`GET /v1/accounts/{accountId}/business-agent/skills/{skillId}`](https://pozy.ai/docs/api/getBusinessAgentSkill) | Get a skill |
| [`PUT /v1/accounts/{accountId}/business-agent/skills/{skillId}`](https://pozy.ai/docs/api/updateBusinessAgentSkill) | Update a skill |
| [`DELETE /v1/accounts/{accountId}/business-agent/skills/{skillId}`](https://pozy.ai/docs/api/deleteBusinessAgentSkill) | Delete a skill |
| [`GET /v1/accounts/{accountId}/business-agent/ui-skills`](https://pozy.ai/docs/api/listBusinessAgentUiSkills) | List UI skills |
| [`POST /v1/accounts/{accountId}/business-agent/ui-skills`](https://pozy.ai/docs/api/createBusinessAgentUiSkill) | Create a UI skill |
| [`GET /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId}`](https://pozy.ai/docs/api/getBusinessAgentUiSkill) | Get a UI skill |
| [`PUT /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId}`](https://pozy.ai/docs/api/updateBusinessAgentUiSkill) | Update a UI skill |
| [`DELETE /v1/accounts/{accountId}/business-agent/ui-skills/{uiSkillId}`](https://pozy.ai/docs/api/deleteBusinessAgentUiSkill) | Delete a UI skill |
| [`GET /v1/accounts/{accountId}/business-agent/connectors`](https://pozy.ai/docs/api/listBusinessAgentConnectors) | List connectors |
| [`POST /v1/accounts/{accountId}/business-agent/connectors`](https://pozy.ai/docs/api/createBusinessAgentConnector) | Create a connector |
| [`GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}`](https://pozy.ai/docs/api/getBusinessAgentConnector) | Get a connector |
| [`PUT /v1/accounts/{accountId}/business-agent/connectors/{connectorId}`](https://pozy.ai/docs/api/updateBusinessAgentConnector) | Update a connector |
| [`DELETE /v1/accounts/{accountId}/business-agent/connectors/{connectorId}`](https://pozy.ai/docs/api/deleteBusinessAgentConnector) | Delete a connector |
| [`POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/credentials`](https://pozy.ai/docs/api/setBusinessAgentConnectorCredentials) | Set connector credentials |
| [`POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/refresh-tools`](https://pozy.ai/docs/api/refreshBusinessAgentConnectorTools) | Refresh MCP connector tools |
| [`GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/logs`](https://pozy.ai/docs/api/getBusinessAgentConnectorLogs) | Get connector failure logs |
| [`GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools`](https://pozy.ai/docs/api/listBusinessAgentConnectorTools) | List connector tools |
| [`POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools`](https://pozy.ai/docs/api/createBusinessAgentConnectorTool) | Create a connector tool |
| [`GET /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId}`](https://pozy.ai/docs/api/getBusinessAgentConnectorTool) | Get a connector tool |
| [`PUT /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId}`](https://pozy.ai/docs/api/updateBusinessAgentConnectorTool) | Update a connector tool |
| [`DELETE /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId}`](https://pozy.ai/docs/api/deleteBusinessAgentConnectorTool) | Delete a connector tool |
| [`POST /v1/accounts/{accountId}/business-agent/connectors/{connectorId}/tools/{toolId}/run`](https://pozy.ai/docs/api/runBusinessAgentConnectorTool) | Run a connector tool once |
| [`GET /v1/accounts/{accountId}/business-agent/budget`](https://pozy.ai/docs/api/getBusinessAgentBudget) | Get usage budgets |
| [`PUT /v1/accounts/{accountId}/business-agent/budget`](https://pozy.ai/docs/api/replaceBusinessAgentBudget) | Replace usage budgets |
| [`POST /v1/accounts/{accountId}/business-agent/test-messages`](https://pozy.ai/docs/api/sendBusinessAgentTestMessage) | Send a test message |
| [`POST /v1/accounts/{accountId}/business-agent/events`](https://pozy.ai/docs/api/sendBusinessAgentEvent) | Send a business event |
| [`GET /v1/accounts/{accountId}/business-agent/events/{eventId}`](https://pozy.ai/docs/api/getBusinessAgentEvent) | Get a business event status |
| [`GET /v1/accounts/{accountId}/business-agent/evals`](https://pozy.ai/docs/api/readBusinessAgentEvals) | Read evaluation data |
| [`POST /v1/accounts/{accountId}/business-agent/evals`](https://pozy.ai/docs/api/startBusinessAgentEvalRun) | Start an evaluation run |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`message.received`](https://pozy.ai/docs/webhooks/events#message-received) | Message received. |
| [`conversation.control_changed`](https://pozy.ai/docs/webhooks/events#conversation-control-changed) | Conversation control changed. |
| [`message.sent`](https://pozy.ai/docs/webhooks/events#message-sent) | Message sent. |
