Sooner or later, a business on the WhatsApp Business Platform wants to change provider — to a different inbox tool, a cheaper plan, or its own integration. The first worry is always the number: will we lose it, our green tick, our message limits, our templates?
Mostly, no. Meta supports moving a phone number between providers, and a lot moves with it. But a lot doesn't, because it never belonged to Meta in the first place. This post sets out exactly what moves, what you have to carry across yourself, the order to do it in, and the Graph API calls involved — all from Meta's migration documentation. The TypeScript snippets are small, and each was run against mocked Graph responses for the cases it describes.
What moves with the number
According to Meta, a migrated number keeps:
- its display name,
- its quality rating,
- its messaging limit (how many people you can start conversations with per day),
- its Official Business Account status (the green tick), per the Embedded Signup migration guide,
- media already uploaded on the number.
Templates are copied too, with conditions. Only templates that are APPROVED with a GREEN quality score are duplicated into the new account. Copies start with an UNKNOWN quality rating, and low-quality, rejected or pending templates are left behind.
What doesn't move
Everything your old provider stored in its own software stays there:
| Data | Moves? | What to do |
|---|---|---|
| Display name, quality, messaging limit, green tick | Yes | Check after migration |
| Approved, high-quality templates | Copied | Re-create anything not copied |
| Rejected, pending or low-quality templates | No | Fix and resubmit |
| Contacts and opt-in records | No | Export from the old provider first |
| Chat history | Not by Meta | Export from the old provider first |
| Chatbots, automations, saved replies | No | Rebuild in the new tool |
| Webhook subscriptions | No | Subscribe your app on the new account |
| Two-step verification PIN | No | Set a new PIN when you register |
Opt-in records deserve a special mention. Meta requires consent before you send business-initiated messages, and that consent usually lives as a flag in your provider's contact list. Export it with the contacts, and map it into your new tool before the first campaign — otherwise you either can't send, or you send to people who never agreed.
Billing changes hands at the switch
Per Meta, messages delivered before the migration completes are charged to the old account, and messages delivered after it are charged to the new one. Make sure the destination account has a valid payment method — or that your new provider has shared its credit line — before you start.
The order of operations
1. Export from the old provider contacts + consent, chat history, any data you'll need
2. Prepare the destination verified business, approved WABA, payment method, app subscribed to webhooks
3. Switch off two-step verification on the number (old provider or Meta Business Suite)
4. Migrate the number Embedded Signup, or the API calls below
5. Verify and register SMS/voice code from the number's owner, then a new 6-digit PIN
6. Check templates find approved ones that didn't copy, and resubmit
7. Rebuild automations chatbots, rules, saved replies, integrations
8. Send a test, then go live
Step 3 is the one that stalls migrations. Meta won't start the move while two-step verification is on, and if the old provider set the PIN, you need them (or Meta Business Suite) to switch it off.
Migrating with the API
Most providers handle the move inside their Embedded Signup flow: the business enters the number, confirms it, and the provider registers it. If you're building your own integration, Meta documents the same steps as API calls. A small helper first:
// graph.ts
const GRAPH = `https://graph.facebook.com/${process.env.GRAPH_API_VERSION ?? "v25.0"}`;
export const graph = async (path: string, token: string, body?: Record<string, unknown>) => {
const res = await fetch(`${GRAPH}/${path}`, {
method: body ? "POST" : "GET",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!res.ok) throw Object.assign(new Error(data.error?.message ?? `Graph ${path} failed`), { code: data.error?.code });
return data;
};
Starting the migration creates the number in the destination account and sends a verification code to the phone:
// migrate-number.ts
import { graph } from "./graph.ts";
// Programmatic migration of a number that is already on the Cloud API with another provider.
// Two-step verification must already be switched off on the source account.
export const startNumberMigration = async (destinationWabaId: string, cc: string, phone: string, token: string) => {
const { id } = await graph(`${destinationWabaId}/phone_numbers`, token, {
cc,
phone_number: phone,
migrate_phone_number: true,
});
await graph(`${id}/request_code`, token, { code_method: "SMS", language: "en_US" });
return id as string; // the phone number ID in the destination account
};
// The number's owner reads the code from the SMS; you also choose a new 6-digit PIN.
export const finishNumberMigration = async (phoneNumberId: string, code: string, pin: string, token: string) => {
await graph(`${phoneNumberId}/verify_code`, token, { code });
await graph(`${phoneNumberId}/register`, token, { messaging_product: "whatsapp", pin });
};
Things worth knowing before you run it:
- The destination account must have business verification and account review approved, and the number must have no pending display-name change.
- Numbers are migrated one at a time — there is no bulk migration.
- Meta's test numbers can't be migrated.
- Migrated numbers can only be registered for the Cloud API.
Checking the templates arrived
Template copying is automatic, but "automatic" isn't "complete". After the move, compare the two accounts and list approved templates that are missing in the new one:
// check-templates.ts
import { graph } from "./graph.ts";
type Template = { name: string; language: string; status: string; quality_score?: { score: string } };
const listTemplates = async (wabaId: string, token: string): Promise<Template[]> => {
const all: Template[] = [];
let path: string | null = `${wabaId}/message_templates?fields=name,language,status,quality_score&limit=100`;
while (path) {
const page = await graph(path, token);
all.push(...page.data);
path = page.paging?.next ? page.paging.next.split(/v\d+\.\d+\//)[1] : null;
}
return all;
};
// Which approved templates from the old account are missing in the new one?
export const missingTemplates = async (sourceWabaId: string, destinationWabaId: string, token: string) => {
const key = (t: Template) => `${t.name}:${t.language}`;
const [source, destination] = await Promise.all([listTemplates(sourceWabaId, token), listTemplates(destinationWabaId, token)]);
const present = new Set(destination.map(key));
return source.filter((t) => t.status === "APPROVED" && !present.has(key(t))).map(key);
};
This needs a token that can still read the old account. For anything missing, resubmit the template — or, if both accounts belong to the same Meta business, use Meta's template migration API (POST /{destination_waba_id}/migrate_message_templates with a source_waba_id), which reports migrated_templates and failed_templates with a reason for each failure, such as "Incorrect category".
On the new provider's side
Once the number is registered in the new account, the rest is rebuilding — and this is where tools differ most. In WAPGROWAI, for example:
- Contacts are imported from a spreadsheet with a column-mapping step, including the campaign consent flag, and duplicates or rows without a valid phone number are skipped and reported.
- Templates already in your WhatsApp account — including the ones Meta copied — are pulled in with a sync from Meta, so they don't need recreating. Templates can also be exported and imported as JSON between accounts; imported ones arrive as drafts and go to Meta for review.
- Automations — rules, chatbots, sequences — are rebuilt in its own builder. Chatbot flows from another tool can't be imported, because each provider uses its own format.
WAPGROWAI's self-serve connection is built for a new number or for coexistence with the WhatsApp Business app; if you're moving a number that's already on the Cloud API, talk to its team before step 3.
Summary
- The number keeps its display name, quality rating, messaging limit and green tick.
- Approved, high-quality templates are copied; check for gaps and resubmit the rest.
- Contacts, consent, chat history and automations are yours to export and rebuild.
- Two-step verification has to be off before anything starts.
- Billing switches to the new account the moment the move completes.
Plan the export first, and the move itself is usually the shortest part of switching.
I work on WAPGROWAI, a WhatsApp Business Platform. Other posts in this series cover webhooks, the 24-hour window, templates and campaigns and coexistence.
Top comments (0)