> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from another provider

> What to move, what to rebuild, and how to cut over

Move your WhatsApp number to Kapso from any Business Solution Provider. Are you on Twilio? Use the [Twilio guide](/docs/migrate/from-twilio) instead.

## Step 1: Connect your number

1. In [WhatsApp Manager](https://business.facebook.com/latest/whatsapp_manager/phone_numbers), check the WABA for other numbers, templates, or assets you still need.
2. Remove the phone number from the WABA.
3. In [Meta Business Settings](https://business.facebook.com/latest/settings/whatsapp_account), open **Accounts → WhatsApp accounts**.
4. If the old WABA sits in your Business Portfolio, remove it.
5. Wait about five minutes.
6. In Kapso, start embedded signup. Create a new WABA when the flow asks.
7. Recreate your templates on the new WABA (see [Step 5](#step-5-templates)). Wait for Meta review.

The number stops sending after step 2, and it can send templates again once Meta approves them on the new WABA. Plan the migration accordingly.

See [Connect WhatsApp](/docs/how-to/whatsapp/connect-whatsapp) for the signup flow.

If Meta blocks the WABA removal over a pending balance, your old provider may still have a credit line attached to it. Meta Direct Support has to clear that. A different number on a new WABA unblocks you in the meantime.

If the reconnect fails, see [coexistence troubleshooting](/docs/how-to/whatsapp/coexistence-troubleshooting).

You can also start on a fresh number. [Instant setup](/docs/platform/phone-numbers/instant-setup) gives you a pre-verified US number, with no SMS verification step.

<Info>
  **Testing first?** Build against a [sandbox](/docs/how-to/whatsapp/use-sandbox-for-testing) number while your current provider still carries production traffic.
</Info>

## Step 2: Get your phone number IDs

```bash theme={null}
curl https://api.kapso.ai/platform/v1/whatsapp/phone_numbers \
  -H "X-API-Key: YOUR_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "1234567890",
      "phone_number_id": "1234567890",
      "name": "Support Line",
      "business_account_id": "98765432109",
      "display_phone_number": "+1 555-123-4567",
      "quality_rating": "GREEN",
      "throughput_tier": "TIER_10K",
      "status": "CONNECTED"
    }
  ]
}
```

Two IDs address different things:

| ID                    | Scopes to                      | Use it for                                              |
| --------------------- | ------------------------------ | ------------------------------------------------------- |
| `phone_number_id`     | one phone number               | sending messages, uploading media, registering webhooks |
| `business_account_id` | the WABA that owns the numbers | templates                                               |

Templates belong to the WABA, not to a number, so every number on that WABA can send them. That is why the template endpoints take the WABA ID in the path, as in `POST /{waba_id}/message_templates`.

## Step 3: Update message sending

For every message type, Kapso uses one endpoint: `POST /{phone_number_id}/messages`. The `type` field says which kind of message you are sending, and a field of that same name carries the content. A text message sets `type: "text"` and puts the body in `text`.

Some providers proxy Meta's Cloud API. If yours did, your payloads already have this shape, and you only change the base URL and the auth header. If your provider had its own message API, use the tabs below as the target shapes.

<Tabs>
  <Tab title="Text">
    Free-form messages, inside the 24-hour window:

    ```bash theme={null}
    curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/1234567890/messages \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "messaging_product": "whatsapp",
        "to": "15551234567",
        "type": "text",
        "text": { "body": "Your order shipped!" }
      }'
    ```

    To reply to a specific message, add `"context": { "message_id": "wamid..." }`. To address a contact that has no phone number, use `recipient` with a [business-scoped user ID](/docs/whatsapp/business-scoped-user-ids) instead of `to`. See [Send text](/docs/whatsapp/send-messages/text).
  </Tab>

  <Tab title="Template">
    Business-initiated messages, outside the 24-hour window. Anything that starts or reopens a conversation has to be an approved template:

    ```bash theme={null}
    curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/1234567890/messages \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "messaging_product": "whatsapp",
        "to": "15551234567",
        "type": "template",
        "template": {
          "name": "order_update",
          "language": { "code": "en_US" },
          "components": [
            {
              "type": "body",
              "parameters": [{ "type": "text", "text": "John" }]
            }
          ]
        }
      }'
    ```

    Parameters are positional and must match the placeholder order. Named parameters work too. See [Simple text templates](/docs/whatsapp/templates/simple-text).
  </Tab>

  <Tab title="Media">
    Media is a message type, not a parameter on a text message:

    ```bash theme={null}
    curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/1234567890/messages \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "messaging_product": "whatsapp",
        "to": "15551234567",
        "type": "image",
        "image": {
          "link": "https://example.com/receipt.png",
          "caption": "Your receipt"
        }
      }'
    ```

    `type` is `image`, `video`, `audio`, `document`, or `sticker`. Documents take a `filename`. Public URLs work directly. To upload bytes first, use `POST /{phone_number_id}/media` and pass the returned `id` instead of `link`. Kapso also ingests from a URL and hands back a Meta media ID via `POST /platform/v1/whatsapp/media`.
  </Tab>

  <Tab title="Interactive">
    Buttons, lists, and CTAs all use Meta's `interactive` object:

    ```bash theme={null}
    curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/1234567890/messages \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "messaging_product": "whatsapp",
        "to": "15551234567",
        "type": "interactive",
        "interactive": {
          "type": "button",
          "body": { "text": "Confirm your appointment?" },
          "action": {
            "buttons": [
              { "type": "reply", "reply": { "id": "btn_yes", "title": "Yes" } },
              { "type": "reply", "reply": { "id": "btn_no", "title": "No" } }
            ]
          }
        }
      }'
    ```

    Taps arrive on `whatsapp.message.received` as an `interactive` message with `button_reply.id`. Swap `interactive.type` for `list` or `cta_url`. See [Send buttons](/docs/whatsapp/send-messages/buttons) and [Send lists](/docs/whatsapp/send-messages/lists).
  </Tab>

  <Tab title="Location and reactions">
    Locations take numbers, not strings:

    ```json theme={null}
    {
      "messaging_product": "whatsapp",
      "to": "15551234567",
      "type": "location",
      "location": {
        "latitude": 37.7749,
        "longitude": -122.4194,
        "name": "San Francisco Office",
        "address": "123 Market St"
      }
    }
    ```

    Reactions point at the message they react to:

    ```json theme={null}
    {
      "messaging_product": "whatsapp",
      "to": "15551234567",
      "type": "reaction",
      "reaction": { "message_id": "wamid...", "emoji": "👍" }
    }
    ```

    See [Send location](/docs/whatsapp/send-messages/location) and [Send reaction](/docs/whatsapp/send-messages/reaction).
  </Tab>

  <Tab title="Read and typing">
    Marking read and showing a typing indicator go to the same endpoint, without a `to`:

    ```bash theme={null}
    curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/1234567890/messages \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "messaging_product": "whatsapp",
        "status": "read",
        "message_id": "wamid.HBgNMTU1NTE0OTU5Nzg1...",
        "typing_indicator": { "type": "text" }
      }'
    ```

    The indicator clears when you send a message, or after about 25 seconds. See [Mark as read](/docs/whatsapp/send-messages/mark-read).
  </Tab>
</Tabs>

The [TypeScript SDK](/docs/whatsapp/typescript-sdk/introduction) wraps all of this, so you do not have to write the envelopes by hand.

## Step 4: Update webhooks

Register per phone number:

```bash theme={null}
curl -X POST https://api.kapso.ai/platform/v1/whatsapp/phone_numbers/1234567890/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "whatsapp_webhook": {
      "url": "https://yourapp.com/webhooks/whatsapp",
      "events": [
        "whatsapp.message.received",
        "whatsapp.message.sent",
        "whatsapp.message.delivered",
        "whatsapp.message.read",
        "whatsapp.message.failed"
      ],
      "secret_key": "your-signing-secret"
    }
  }'
```

Payloads carry `message`, `conversation`, and `phone_number_id`, with Kapso's own fields under `message.kapso`. The event name arrives in the `X-Webhook-Event` header. Full shapes in [Message events](/docs/platform/webhooks/message-events).

`conversation` threads the messages for you, but it does not track the 24-hour window. A send can fail with `131047` while `conversation.status` is `active`. Read `conversation.kapso.last_inbound_at`, or handle the rejection and fall back to a template.

`from` is not always present. WhatsApp can identify a contact with `business_scoped_user_id` instead. See [business-scoped user IDs](/docs/whatsapp/business-scoped-user-ids).

For connection lifecycle events (a customer finishing a setup link, Meta disabling a WABA), use [project webhooks](/docs/platform/webhooks/project-webhooks) instead. You configure those once for the whole project.

<Note>
  Does your current provider forward raw Meta payloads? Register the webhook with `"kind": "meta"` and Kapso forwards Meta's exact payload with no reshaping. Your existing parser keeps working.
</Note>

Kapso signs the raw request body with HMAC-SHA256 and sends the hex digest in `X-Webhook-Signature`:

```javascript theme={null}
const crypto = require('crypto');

// express.raw, not express.json: Kapso signs the exact bytes it sent, and
// re-serializing a parsed body does not reproduce them
app.post('/webhooks/whatsapp', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(req.headers['x-webhook-signature'] ?? '', 'utf8');

  // timingSafeEqual throws on length mismatch, so compare lengths first
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  res.sendStatus(200);
});
```

Delivery is at-least-once, retried at 10s, 40s, and 90s. Dedupe on `X-Idempotency-Key`. See [Security](/docs/platform/webhooks/security) for Python and Ruby, and [Advanced](/docs/platform/webhooks/advanced) for buffering and ordering.

## Step 5: Templates

Templates belong to the WABA at Meta, not to your provider. If you build templates in WhatsApp Manager, pull them into Kapso from **WhatsApp → Templates → Sync from WhatsApp**.

The path takes the WABA ID, not the phone number ID:

```bash theme={null}
curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/98765432109/message_templates \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order_update",
    "category": "UTILITY",
    "language": "en_US",
    "components": [
      {
        "type": "BODY",
        "text": "Hi {{1}}, your order is confirmed.",
        "example": { "body_text": [["John"]] }
      }
    ]
  }'
```

Meta reviews them, usually within 24 hours. See [Template lifecycle](/docs/whatsapp/templates/lifecycle).

## Multi-tenant setups

If you message on behalf of your own customers, each one becomes a Kapso customer that connects its own number through a setup link:

```bash theme={null}
curl -X POST https://api.kapso.ai/platform/v1/customers \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customer": {"name": "Acme Corporation", "external_customer_id": "CUS-12345"}}'

curl -X POST https://api.kapso.ai/platform/v1/customers/CUSTOMER_ID/setup_links \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "setup_link": {
      "success_redirect_url": "https://your-app.com/whatsapp/success",
      "meta_billing_mode": "partner_managed"
    }
  }'
```

Send your customer the returned `url`. They log in with Facebook and connect in about five minutes. You then get `whatsapp.phone_number.created` on your project webhook, with the `customer.id` and `phone_number_id`. See [Onboard customers](/docs/platform/customer-guide).

One API key covers every customer. Migrate one customer at a time.

## Cutover

Migrate one number at a time. A number's WhatsApp registration and webhook routing move as a unit, so there is no gradual split per number.

| Phase   | Actions                                                                                                                                            |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prep    | Create the project and API key, build against a [sandbox](/docs/how-to/whatsapp/use-sandbox-for-testing) number, export history from your provider |
| Pilot   | Move one low-traffic number end to end. Budget for the time the number is down, and for Meta's template review                                     |
| Rollout | Migrate remaining numbers in batches; for multi-tenant, one customer at a time                                                                     |
| Cutoff  | Stop sends on the old provider for each migrated number, then close the account                                                                    |

Meta bills message charges against the WABA either way, per delivered template message. See [pricing](/docs/whatsapp/pricing-faq).

## Troubleshooting

| Symptom                               | Fix                                                                                                                         |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Number cannot be connected            | It is still on the provider's WABA. Migrate it in WhatsApp Manager, and ask the provider to release it if they hold the PIN |
| `401 Unauthorized`                    | Send `X-API-Key: YOUR_API_KEY`                                                                                              |
| `(#131047)` re-engagement message     | Outside the 24-hour window. Send an approved template                                                                       |
| Template not found                    | Templates are addressed by `name` + `language`, and must exist on this WABA. Sync or recreate them                          |
| Templates missing after the move      | They belonged to the old WABA. Recreate them on the new one and wait for review                                             |
| Webhook signature mismatch            | HMAC-SHA256 over the raw body, checked against `X-Webhook-Signature`                                                        |
| Marketing template refused with `422` | The contact stopped marketing on that number. See [marketing opt-outs](/docs/whatsapp/templates/marketing-opt-outs)         |
| `429`                                 | Back off on `Retry-After`; see [rate limits](/api/rate-limits)                                                              |

## Need help

* [Send messages](/docs/whatsapp/send-messages/text) and [webhooks](/docs/platform/webhooks/overview)
* [API reference](/api/introduction)
* [WhatsApp support](https://wa.me/16266694464?text=Hi!%20I%20need%20help%20migrating%20to%20Kapso)
