# Phone numbers for WhatsApp

Buy a WhatsApp-ready number, bring your own, verify and register it with Meta, check that it is alive and follow its lifecycle events.

Every WhatsApp account sits on a phone number. You can buy one through Pozy, which arrives already verified with Meta and can also make calls and send SMS, or connect a number you already own. This page covers the WhatsApp side of numbers: getting one ready, keeping it registered and knowing when it stops working. Searching inventory, country rules, KYC and porting are in [Phone, SMS & voice](https://pozy.ai/docs/telephony).

## Buy a number for WhatsApp

Call [`POST /v1/phone-numbers/purchase`](https://pozy.ai/docs/api/purchasePhoneNumber) with the profile the number should belong to. `connectWhatsapp` is `true` by default, so the purchase hands straight over to [WhatsApp signup](https://pozy.ai/docs/whatsapp#connect-with-embedded-signup), where the new number appears as **Verified** in Meta's picker.

```bash cURL
curl -X POST https://api.pozy.ai/v1/phone-numbers/purchase \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6710c4e2b9a1f03d8e5c7a21",
    "country": "US",
    "numberType": "local",
    "areaCode": "415",
    "wantsWhatsapp": true,
    "purchaseIntentId": "bakery-main-line"
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/phone-numbers/purchase", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    profileId: "6710c4e2b9a1f03d8e5c7a21",
    country: "US",
    numberType: "local",
    areaCode: "415",
    wantsWhatsapp: true,
    purchaseIntentId: "bakery-main-line",
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);

console.log(data);
```
```python Python
import os
import requests

H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
resp = requests.post(
    "https://api.pozy.ai/v1/phone-numbers/purchase",
    headers=H,
    json={
        "profileId": "6710c4e2b9a1f03d8e5c7a21",
        "country": "US",
        "numberType": "local",
        "areaCode": "415",
        "wantsWhatsapp": True,
        "purchaseIntentId": "bakery-main-line",
    },
    timeout=30,
)
print(resp.json())
```

`purchaseIntentId` makes the purchase safe to retry: sending the same id again returns the number you already bought (`status: "already_purchased"`) instead of buying a second one.

| Country type | What happens |
| --- | --- |
| Instant (the US and others) | The number is ready in seconds |
| Regulated | A one-time [KYC form](https://pozy.ai/docs/telephony/kyc) first. The number activates in 1 to 3 business days and `whatsapp.number.activated` fires when it is ready. |

A number needs a payment method on the workspace. Without one the purchase returns `402` `payment_required`. Pricing is per number per month ([WhatsApp pricing](https://pozy.ai/docs/whatsapp/pricing)).

> **Older paths:**
> 
> The `/v1/whatsapp/phone-numbers/*` purchase, search and KYC routes still work but are deprecated. Use the `/v1/phone-numbers/*` routes, which take the same inputs.

### Add WhatsApp to a number you bought for calls or SMS

If a Pozy number was bought without WhatsApp, start Meta's verification with [`POST /v1/phone-numbers/{id}/whatsapp/request-code`](https://pozy.ai/docs/api/requestPhoneNumberWhatsAppCode) (`method` `SMS` or `VOICE`). Pozy receives the code on the number itself, so you do not have to relay it. Then connect the number through signup as usual.

If Meta refuses the number for WhatsApp, a number that is already live keeps working for calls and SMS and the call returns `409` `number_not_whatsapp_eligible`. A number that was never live is swapped for an eligible one at no cost, and the response says `replaced: true` with the `newPhoneNumber`.

### Move a number to another profile

A Pozy number belongs to one profile, and connecting it from any other profile returns `409`. To re-home it, call [`PATCH /v1/whatsapp/phone-numbers/{id}/profile`](https://pozy.ai/docs/api/moveWhatsAppNumberToProfile) with the new `profileId`. That moves the number and every account built on it (WhatsApp, calls, SMS) together. `id` is the number id from `GET /v1/phone-numbers`, not an account id.

## Bring your own number

A number on your own carrier connects through [Embedded Signup or a Meta token](https://pozy.ai/docs/whatsapp#pick-the-kind-of-number). It works for WhatsApp messages and calls, but Pozy cannot add SMS or phone-network calling to it, because there is no Pozy line behind it. `GET /v1/phone-numbers` lists it under `connected`, not `numbers`.

Meta occasionally asks a number you brought from another provider to verify again. When that happens:

1. Request a code with [`POST /v1/accounts/{accountId}/whatsapp/request-code`](https://pozy.ai/docs/api/requestWhatsAppVerificationCode) (`SMS` or `VOICE`). The code goes to the business's own phone, so someone there has to read it. You can request one every 10 minutes, and Meta adds its own cooldown.
2. Submit it with [`POST /v1/accounts/{accountId}/whatsapp/verify-code`](https://pozy.ai/docs/api/verifyWhatsAppNumber).
3. Finish with [`POST /v1/accounts/{accountId}/whatsapp/register`](https://pozy.ai/docs/api/registerWhatsAppNumber). Verifying alone does not put the number back on the Cloud API.

## Register a number that has a PIN

If the number has two-step verification turned on, the connect flow cannot register it on its own and Meta rejects the registration (error `133005`). The account still looks connected, but every send fails with `(#200) You do not have the necessary permission`.

Run the registration again with the right PIN. Pozy uses the PIN for this call only and does not store it.

```bash cURL
curl -X POST https://api.pozy.ai/v1/accounts/6712a9d4c0e3b58f1a7d2e64/whatsapp/register \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "pin": "205718" }'
```

A `422` means Meta still refused, most often a wrong PIN. Check it in WhatsApp Manager and try again.

## Check that a number is alive

A WhatsApp channel can die without much warning, for example when a coexistence number is disconnected from the phone. Do not rely on the absence of an `account.disconnected` event; check directly.

| Check | What it tells you |
| --- | --- |
| [`GET /v1/whatsapp/number-info`](https://pozy.ai/docs/api/getWhatsAppNumberInfo) | A live read from Meta. `200` means Meta is serving the number. `400` with Meta error 100, subcode 33 means it is gone until you reconnect. |
| [`GET /v1/accounts/{accountId}/health`](https://pozy.ai/docs/api/getAccountHealth) | Runs the same live read and reports it as `platformConnection.status`: `connected`, `disconnected` or `unknown` (the read failed for another reason, such as a timeout). A valid token does not mean the channel is alive. |
| [`GET /v1/whatsapp/business-profile`](https://pozy.ai/docs/api/getWhatsAppBusinessProfile) | Not a liveness check. It can return a full profile for a dead number. |
| `platformStatus` in [`GET /v1/accounts`](https://pozy.ai/docs/api/listAccounts) | A stored value refreshed by periodic checks. It can be hours or days old. Compare it with `metadata.metaStatusCheckedAt` before acting on it. |

Health also reports `messagingRestriction` when Meta refuses sends for a reason other than the channel itself:

| Meta code | Reason |
| --- | --- |
| `131042` | A payment problem on the WABA |
| `131031` | The WhatsApp Business Account is locked |
| `368` | A policy block |

The restriction appears on the first refused send and clears on the next delivered message.

## Number lifecycle

Numbers bought through Pozy move through statuses, and Pozy sends an event on each step. The event names start with `whatsapp.number.` for historical reasons, but they fire for every Pozy number, including ones used only for calls or SMS.

| Status | Reached by | Event |
| --- | --- | --- |
| `pending_regulatory` | KYC submitted in a regulated country | `whatsapp.number.kyc_submitted` (hosted KYC links only) |
| `pending_regulatory` (blocked) | The regulator wants more information or an ID check | `whatsapp.number.action_required`, `whatsapp.number.verification_required` |
| `regulatory_declined` | The regulator declined the order. You are not billed. Fix and resubmit with [remediation](https://pozy.ai/docs/api/remediatePhoneNumber). | `whatsapp.number.declined` |
| `active` | Setup finished; ready to connect | `whatsapp.number.activated` |
| `suspended` | For example a failed payment (`reason: "payment_failed"`) | `whatsapp.number.suspended` |
| `active` again | The issue was fixed | `whatsapp.number.reactivated` |
| `released` | Released by you or after an unresolved suspension. Final. | `whatsapp.number.released` |

Numbers in instant countries skip the review states and go straight to `active`.

## Release a number

[`DELETE /v1/phone-numbers/{id}`](https://pozy.ai/docs/api/releasePhoneNumber) releases a number you bought. It disconnects the WhatsApp account on it, stops the monthly charge and makes the number unusable. This cannot be undone.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/phone-numbers`](https://pozy.ai/docs/api/listPhoneNumbers) | List phone numbers |
| [`POST /v1/phone-numbers/purchase`](https://pozy.ai/docs/api/purchasePhoneNumber) | Purchase phone number |
| [`GET /v1/phone-numbers/{id}`](https://pozy.ai/docs/api/getPhoneNumber) | Get phone number |
| [`DELETE /v1/phone-numbers/{id}`](https://pozy.ai/docs/api/releasePhoneNumber) | Release phone number |
| [`POST /v1/phone-numbers/{id}/whatsapp/request-code`](https://pozy.ai/docs/api/requestPhoneNumberWhatsAppCode) | Request the WhatsApp verification code for a number |
| [`PATCH /v1/whatsapp/phone-numbers/{id}/profile`](https://pozy.ai/docs/api/moveWhatsAppNumberToProfile) | Move a number to another profile |
| [`GET /v1/whatsapp/number-info`](https://pozy.ai/docs/api/getWhatsAppNumberInfo) | Get number status |
| [`POST /v1/accounts/{accountId}/whatsapp/request-code`](https://pozy.ai/docs/api/requestWhatsAppVerificationCode) | Request a Meta re-verification code for a BYO WhatsApp number |
| [`POST /v1/accounts/{accountId}/whatsapp/verify-code`](https://pozy.ai/docs/api/verifyWhatsAppNumber) | Verify the Meta re-verification code for a BYO WhatsApp number |
| [`POST /v1/accounts/{accountId}/whatsapp/register`](https://pozy.ai/docs/api/registerWhatsAppNumber) | Register a connected WhatsApp number on the Cloud API |
| [`GET /v1/accounts/{accountId}/health`](https://pozy.ai/docs/api/getAccountHealth) | Check account health |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`account.disconnected`](https://pozy.ai/docs/webhooks/events#account-disconnected) | Account disconnected. |
| [`whatsapp.number.activated`](https://pozy.ai/docs/webhooks/events#whatsapp-number-activated) | WhatsApp number activated. |
| [`whatsapp.number.declined`](https://pozy.ai/docs/webhooks/events#whatsapp-number-declined) | WhatsApp number declined. |
| [`whatsapp.number.action_required`](https://pozy.ai/docs/webhooks/events#whatsapp-number-action-required) | WhatsApp number action required. |
| [`whatsapp.number.verification_required`](https://pozy.ai/docs/webhooks/events#whatsapp-number-verification-required) | WhatsApp number verification-required. |
| [`whatsapp.number.suspended`](https://pozy.ai/docs/webhooks/events#whatsapp-number-suspended) | WhatsApp number suspended. |
| [`whatsapp.number.reactivated`](https://pozy.ai/docs/webhooks/events#whatsapp-number-reactivated) | WhatsApp number reactivated. |
| [`whatsapp.number.released`](https://pozy.ai/docs/webhooks/events#whatsapp-number-released) | WhatsApp number released. |
| [`whatsapp.number.kyc_submitted`](https://pozy.ai/docs/webhooks/events#whatsapp-number-kyc-submitted) | WhatsApp number KYC submitted. |
| [`phone_number.stock_available`](https://pozy.ai/docs/webhooks/events#phone-number-stock-available) | Phone-number stock available. |
