---
title: "Connect a number from your multi-partner solution"
canonical: "https://bird.com/docs/api/reference/create-organization-workspace-whatsapp-number"
---

# Connect a number from your multi-partner solution

`POST /v1/organization/workspaces/{workspace_id}/whatsapp/numbers`

Connects a number that your own Embedded Signup onboarded through your
multi-partner solution with Bird. Bird confirms that the WhatsApp Business
Account came through your solution and holds the number, then sets the
number up in the workspace you name. Checking that your customer owns the
account is up to you.

Setup continues in the background. Poll the returned number in the same
workspace, which an organization credential has to select explicitly. It
can send once its status is `connected`, and
`failed` means setup stopped, with `error` saying why. Calling
again for a number already connected in the workspace runs its setup again,
and the number cannot send until it reconnects.

Call it with an organization credential: a master key, or a signed-in
owner or organization admin. A workspace API key or a workspace member
gets a 403. Only organizations with an active multi-partner solution can
call this.
An organization not enabled for multi-partner solutions gets a 403 and
connects numbers with Embedded Signup in the Bird dashboard. An enabled one
whose solution is not active, because it has none yet or Bird is still
reviewing it, gets a 409.

## Servers

- `https://{region}.platform.bird.com`: Regional API endpoint. Use the host for the region your organization is hosted in. Official Bird SDKs and the CLI select it automatically from your API key, so you rarely need to set it by hand.
  - `region`: `us1`, `eu1`. Default: `us1`. The region your organization's data is hosted in.
- `https://platform.bird.com`: Region-independent endpoint for authentication and account administration.
- `http://localhost:8080`: Local development.

## Authentication

Use one authentication option:

- `BearerAuth`: HTTP bearer authentication. Pass the API key as a bearer token in the `Authorization` header. Workspace
keys use `bk_{region}_*`; organization master keys use `bm_{region}_*`.
The prefix identifies the region and selects the
API endpoint. Official Bird SDKs and the CLI derive the region from the key.

## Code samples

**TypeScript**

```ts
const number = await bird.organization.workspaces.whatsappNumbers.create("ws_01krdgeqcxet5s7t44vh8rt9mg", {
  waba: "102290129340398",
  meta_phone_number_id: "109364591979012",
  name: "Northwind Support",
});
console.log(number.id, number.status);
```

Examples: [TypeScript](/docs/api/reference/create-organization-workspace-whatsapp-number.ts.md) · [Python](/docs/api/reference/create-organization-workspace-whatsapp-number.py.md) · [Go](/docs/api/reference/create-organization-workspace-whatsapp-number.go.md) · [PHP](/docs/api/reference/create-organization-workspace-whatsapp-number.php.md) · [cURL](/docs/api/reference/create-organization-workspace-whatsapp-number.curl.md)

## Example response `202`

```json
{
  "id": "wan_01krdgeqcxet5s7t44vh8rt9mg",
  "waba": "102290129340398",
  "phone_number": "+15550001234",
  "number_id": "nda_01krdgeqcxet5s7t44vh8rt9mg",
  "meta_preverified_id": "106540352242922",
  "verified_until": "2026-11-19T10:22:31Z",
  "name": "Sales EU",
  "scope": "workspace",
  "data_localization_region": "DE",
  "status": "connected",
  "next": [
    {
      "kind": "operation"
    }
  ],
  "error": {
    "code": "registration_pin_rejected",
    "description": "Cannot Create Certificate: Please ensure two-factor authentication is disabled.",
    "meta_error_code": "2388001"
  },
  "finish_setup_url": "https://bird.com/dashboard/w/ws_01krdgeqcxet5s7t44vh8rt9mg/whatsapp/numbers?finish_setup_number=wan_01krdgeqcxet5s7t44vh8rt9mg",
  "quality_rating": "green",
  "messaging_limit": "tier_250",
  "throughput_level": "standard"
}
```

## Path parameters

- `workspace_id` (string, required): The workspace the number belongs to. Starts with `ws_`.

## Header parameters

- `Idempotency-Key` (string, optional)

  Client-supplied key. On operations supporting request deduplication, a retained
  response is replayed for duplicate requests with the same key within the
  idempotency window (3 hours by default). This protection requires a workspace,
  organization, or staff-account scope. User-only and unscoped unauthenticated operations,
  streams, and operations with a separate replay contract do not use this
  response replay.

  On a supported operation, if idempotency protection is unavailable before execution, the API returns
  `503 IdempotencyUnavailable` (E01033) without executing this attempt. Retry with
  backoff using the same key and request. An operation that takes effect before
  its response is retained can still execute again on retry.

  Two distinct 409 errors signal misuse:

  - `request_in_progress` (E01004): The same key is currently being
    processed by a concurrent request. Wait briefly and retry. The lock expires within 30 seconds.
  - `idempotency_key_reuse` (E01005): The same key has already completed
    against a different request body or method. Generate a new key.

  Recommended key format is `<event-type>/<entity-id>` (for example `welcome-user/usr_abc123`).
- `X-Organization-Id` (string, optional): Organization context for the request. Required for dashboard authentication. An API key or access token carries its own organization, so send either that organization or no header at all; a different one is rejected.

## Request body

- `waba` (string, required): The WhatsApp Business Account your Embedded Signup returned, from the session info's `messaging_account_ids`.
- `meta_phone_number_id` (string, required): The phone number your Embedded Signup returned, from the session info's `phone_number_id`.
- `name` (string): The workspace's own label for the number. Leave it out and the number's own phone number stands in. A number already connected in the workspace keeps its label unless you pass one. This has no bearing on what WhatsApp displays to people the number messages.
- `data_localization_region` (string)

  Store this number's message content at rest in a specific country, given as its two-letter ISO 3166 code. Leave it out to use WhatsApp's default storage. This cannot be changed once the number is connected, so set it now if your customer requires it.

  Possible values: `AU`, `ID`, `IN`, `JP`, `SG`, `KR`, `DE`, `CH`, `GB`, `BR`, `BH`, `ZA`, `AE`, `CA`

## Response body

- `id` (string, required): Unique identifier for the connected number.
- `waba` (string): The WhatsApp Business Account this number is connected under. Present only for a number your workspace connected itself.
- `phone_number` (nullable string, required): The number in E.164 format. Null only while the number itself is not yet known: a number your workspace holds carries its E.164 from the moment setup starts, so a value here does not mean the number can send. Check `status` for connection state.
- `number_id` (string): The number you hold with us that this WhatsApp number was connected from, as its id in GET /v1/numbers. Absent for a number you brought yourself.
- `meta_preverified_id` (string): WhatsApp's own id for the number while we prepare it for you. We hand it to WhatsApp when you finish signing up, so WhatsApp knows this is the number we already verified for you. Absent for a number you brought yourself. WhatsApp discards the id once signup claims it, so an id still shown here on a connected number no longer names anything.
- `verified_until` (string): When we expect WhatsApp to release this pre-verified number if you have not finished signup by then. Present only while the number is `awaiting_signup`. Absent for a number pre-verified before we started recording this moment, and for a number you brought yourself.
- `name` (string, required): Your workspace's own label for this number, given when it was connected and changeable afterwards. It has no bearing on what WhatsApp displays to people the number messages; `GET /v1/whatsapp/numbers/{number_id}/profile` returns that as `display_name`. For a number we operate on your behalf, this is our own label instead and cannot be changed.
- `scope` (string, required)

  Whether the number sends under a WhatsApp Business Account we operate on your behalf (`system`) or one your workspace connected itself (`workspace`).

  Possible values: `system`, `workspace`
- `data_localization_region` (string)

  The country this number's message content is stored at rest in, as its two-letter ISO 3166 code. Absent when it uses WhatsApp's default storage. It can differ from the region requested at connection when WhatsApp requires a particular country for the number.

  Possible values: `AU`, `ID`, `IN`, `JP`, `SG`, `KR`, `DE`, `CH`, `GB`, `BR`, `BH`, `ZA`, `AE`, `CA`
- `status` (string, required)

  WhatsApp's own state for this number as of `meta_synced_at`, except for the three states we answer ourselves because WhatsApp holds nothing to report. A connection we are still verifying reads `preparing`, one waiting for someone to finish signup reads `awaiting_signup`, and a permanently refused one reads `failed`, with `error` saying why. `pending` is WhatsApp's own token for a number it does not hold as registered, and is also what a number with no stored WhatsApp status reads, including after setup completes, so it does not by itself establish whether setup is complete. A number we operate on your behalf reads `connected` as our own assertion rather than a reading from WhatsApp for every number we ship today; that tier carries no `meta_synced_at`. A connected virtual +1 555 or +1 711 number still has a daily sending limit until its current name is approved. Read `display_name_status` from the live profile for display name approval.

  Possible values (may grow over time): `awaiting_signup`, `banned`, `connected`, `deleted`, `disconnected`, `failed`, `flagged`, `migrated`, `pending`, `preparing`, `rate_limited`, `restricted`
- `next` (array of object)

  What to do next about this number, given the state it is in. Each entry names one
  action and says why it is worth taking, so you can act on this response without
  working out the order yourself. Present on reads that compute it: an empty list
  means there is nothing to do, and the field is absent entirely on responses that
  do not report next actions.

  While `status` is `awaiting_signup` this carries the browser step that finishes
  the connection, because embedded signup sits behind an OAuth screen no API call
  can stand in for.
- `next.kind` (string, required)

  What you do about this step.

  - `operation`: call the operation named in `operation`, then
    read again.
  - `external`: act somewhere this API does not reach, then read
    again.
  - `wait`: nothing is asked of you, so read again later.
  - `terminal`: nothing you do resolves this, so stop retrying.

  Tolerate a value you do not recognize: show the `description` and
  offer no action.

  Possible values (may grow over time): `operation`, `external`, `wait`, `terminal`
- `next.description` (string, required): A short, human-readable label for the step, suitable for display.
- `next.operation` (string): The operationId to call. Present only when `kind` is `operation`. The operation's own schema says how to call it; this says only which one, and what to address it with.
- `next.params` (object): The parameters that address the operation, by name: `{"sender_id": "…"}` for an operation on `/v1/sms/senders/{sender_id}/requirements`. A parameter the operation takes in its query string is given the same way, so an operation addressed as `?subject_id=` carries `{"subject_id": "…"}`. Every parameter the call needs is here, whether its value came from the thing you were acting on or is fixed for this step, so you can make the call from this object alone. Present only when `kind` is `operation` and the operation names a subject. A request body, when the operation takes one, is described by the operation's own schema and never appears here.
- `next.url` (string): A URL to open. Present only when `kind` is `external`, and only when the step has one. An external step whose `description` says to go and do something with no URL to open is normal.
- `error` (object): Why this number's connection was refused for good. Present only while `status` is `failed`. A retryable step records its cause on a still-`pending` number without setting this field, because that cause is not a refusal yet, so a connection you are still waiting on reports no error here.
- `error.code` (string, required)

  Standardized failure reason.

  Possible values (may grow over time): `registration_pin_rejected`, `registration_pin_rate_limited`, `registration_attempts_exhausted`, `number_verification_required`, `number_not_registered`, `number_already_linked`, `number_already_in_use`, `verification_code_not_received`, `verification_rate_limited`, `business_account_locked`, `business_verification_required`, `credit_currency_mismatch`, `billing_close`, `permission_denied`, `invalid_request`, `internal_error`
- `error.description` (string): Why the connection failed: WhatsApp's own words, in the language of the account it refused, when WhatsApp answered; our own explanation when the number was refused before WhatsApp was asked; a generic sentence when WhatsApp refused without giving a reason. Absent when the attempt failed without ever reaching WhatsApp, which leaves `code` as the only account of the failure. Show it to the person who owns the number; never match on its text.
- `error.meta_error_code` (nullable string): WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Null when WhatsApp did not provide a code. Treat it as an opaque string.
- `finish_setup_url` (string): Where a person finishes connecting this number, present only while `status` is `awaiting_signup`. Finishing means completing WhatsApp's embedded signup, which is a browser flow behind an OAuth screen: it cannot be done over the API, so open this link and have someone with access to the workspace complete it. The number is offered to them already verified. Once they finish, `status` moves on and this link is no longer returned.
- `quality_rating` (string)

  WhatsApp's quality rating for this number as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf.

  Possible values (may grow over time): `green`, `yellow`, `red`, `unknown`
- `messaging_limit` (string)

  The messaging limit WhatsApp applied to this number's business portfolio as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf.

  Possible values (may grow over time): `tier_50`, `tier_250`, `tier_1k`, `tier_10k`, `tier_100k`, `tier_unlimited`
- `throughput_level` (string)

  The send rate WhatsApp allowed this number as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf.

  Possible values (may grow over time): `standard`
- `is_official_business_account` (boolean): Whether WhatsApp grants this number Official Business Account status as of `meta_synced_at`. Absent until WhatsApp has reported it, and always absent for a number we operate on your behalf. WhatsApp grants the status per number, so two numbers on one WhatsApp Business Account can differ. The status also decides whether a rename is possible here: a number that has it cannot be renamed through `PATCH /v1/whatsapp/numbers/{number_id}/profile` at all, and has to be renamed through WhatsApp support instead.
- `is_on_business_app` (boolean): Whether this number is also in use on the WhatsApp Business app, so the business keeps sending one-to-one messages from the app while you send through the API. Absent until WhatsApp has reported it, and always absent for a number we operate on your behalf.
- `meta_synced_at` (string): When this number's state was last read from WhatsApp. `status`, `quality_rating`, `messaging_limit`, `throughput_level`, and `is_official_business_account` all belong to that reading rather than representing live values. We re-read roughly hourly, so a change at WhatsApp can be up to an hour old here. Absent for a number we have never read back and for a number we operate on your behalf.
- `pre_verification_requested_at` (string): When we last asked WhatsApp to send this number a verification code, which we do only for a number your workspace connected itself from a number you hold with us. Absent for a number we operate on your behalf, and for one you connected through Embedded Signup with a code you read yourself. Wait a few hours after this before repairing a number whose verification failed: WhatsApp rotates the routes it verifies over during that period, and throttles a number asked repeatedly in a short window. Distinct from `updated_at`, which any change to the number moves.
- `created_at` (string, required): When this number was submitted for connection.
- `updated_at` (string, required): When this number was last changed.

## Related resources

- [Should I use a Bird SDK or call the API directly?](/explained/platform/should-i-use-an-sdk-or-call-the-api-directly) (answer)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=api-basics)
