openapi: 3.2.0 info: title: Bird Whatsapp Numbers API version: 1.0.0 description: 'The Bird API: one REST API for email, SMS, WhatsApp, verification, and Realtime.' servers: - url: https://{region}.platform.bird.com description: '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. ' variables: region: default: us1 enum: - us1 - eu1 description: The region your organization's data is hosted in. - url: https://platform.bird.com description: Region-independent endpoint for authentication and account administration. - url: http://localhost:8080 description: Local development. security: - BearerAuth: [] tags: - name: whatsapp-numbers description: Connect phone numbers from Meta's embedded signup flow and check their WhatsApp setup status. paths: /v1/whatsapp/numbers: get: operationId: listWhatsAppNumbers x-snippet-key: whatsapp.numbers.list summary: List WhatsApp numbers description: 'Returns a paginated list of the WhatsApp numbers your workspace can send from. The list includes both platform-managed numbers and numbers on a WhatsApp Business Account you connected. Each item reports its WhatsApp status, quality rating, business portfolio messaging limit, allowed send rate, and Official Business Account status. Page through the full set with the response cursors. A cursor naming a platform-managed number is rejected once the active filters would exclude it, including a `scope=system` cursor for a number that is not platform-managed. Start again without `starting_after` or `ending_before` whenever you change `phone_number`, `waba`, `status`, or `scope`.' tags: - whatsapp-numbers security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - dashboard - command parameters: - name: waba in: query required: false description: Filter to a single WhatsApp Business Account by its Meta-assigned ID. Use the `waba` of a connected WhatsApp Business Account, or the `waba` on a number this list returns. A platform-managed number belongs to no WhatsApp Business Account and is never returned when this is set, so pairing it with `scope=system` always returns an empty page. An account this workspace does not hold returns an empty page. schema: type: string minLength: 1 example: '102290129340398' - name: phone_number in: query required: false description: Filter to a single number, given in E.164 format. The value is normalized before matching, so `+31612340001` and `+31 6 1234 0001` are the same filter. A value that cannot be normalized is matched exactly as given. A number this workspace cannot send from returns an empty page, rather than being rejected. schema: type: string minLength: 1 example: '+31612340001' - name: status in: query required: false description: 'Filter by the number''s WhatsApp state: the `status` a number in this list carries. Every value matches that status exactly, so repeat the parameter for each state you want. `pending` covers both a number WhatsApp reports as not registered and one it has reported no state for at all, while the two states before that keep their own values: a number we are still verifying carries `preparing`, and one waiting for someone to finish signup carries `awaiting_signup`. Asking for all three reaches every connection that has not finished, plus any finished one WhatsApp has not reported on yet. `failed` matches a connection refused permanently. A value this vocabulary does not recognize matches nothing, rather than being rejected. Omit to return numbers in every state.' style: form explode: true schema: type: array minItems: 1 uniqueItems: true items: $ref: '#/components/schemas/WhatsAppNumberStatus' - name: scope in: query required: false description: 'Filter by ownership tier: `system` for platform-managed numbers and `workspace` for numbers your workspace connected. Omit to return both. A `system` number belongs to no WhatsApp Business Account, so pairing this with `waba` always returns an empty page.' schema: $ref: '#/components/schemas/WhatsAppNumberScope' - name: sort in: query required: false description: Field to sort by. schema: $ref: '#/components/schemas/WhatsAppNumberSortField' - $ref: '#/components/parameters/OrderDesc' - $ref: '#/components/parameters/PaginationLimit' - $ref: '#/components/parameters/StartingAfter' - $ref: '#/components/parameters/EndingBefore' responses: '200': description: A page of the WhatsApp numbers your workspace can send from. content: application/json: schema: $ref: '#/components/schemas/WhatsAppNumberList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - attio - cli - make - mcp - sdk /v1/whatsapp/numbers/{number_id}: get: operationId: getWhatsAppNumber x-snippet-key: whatsapp.numbers.get summary: Get a WhatsApp number description: 'Returns a WhatsApp number connected to the workspace and its state as of `meta_synced_at`. Poll this after completing embedded signup to follow the connection. The reported status can lag send availability by up to one hour. `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. Other WhatsApp states include `connected`, `disconnected`, and `flagged`. A `failed` status means connection setup ended permanently, and `error` says why; a failure still being retried leaves the number `pending` and sets no `error`.' tags: - whatsapp-numbers security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - dashboard - command parameters: - name: number_id in: path required: true description: ID of the WhatsApp number (`wan_` prefix), as returned by the number list. schema: $ref: '#/components/schemas/WhatsAppNumberID' responses: '200': description: The WhatsApp number. content: application/json: schema: $ref: '#/components/schemas/WhatsAppNumber' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk /v1/whatsapp/numbers/{number_id}/events: get: operationId: listWhatsAppNumberEvents summary: List WhatsApp number events description: 'Returns the number''s activity history, newest first by default: - The number being added, and every change of its status. - A messaging-limit change reported by WhatsApp. - A display-name review decision. - A quality-rating change, observed when Bird next reads the number. Use it to see when and why a number''s status, limit, name, or quality changed, rather than polling Get a WhatsApp number. A number ID that does not belong to the workspace, or no longer exists, returns `404`.' tags: - whatsapp-numbers x-audiences: - public - dashboard - command x-snippet-key: whatsapp.numbers.list_events security: - BearerAuth: [] - CookieAuth: [] parameters: - name: number_id in: path required: true description: ID of the WhatsApp number whose events to list. schema: $ref: '#/components/schemas/WhatsAppNumberID' - name: sort in: query required: false description: Field to sort by. Defaults to `created_at`. schema: $ref: '#/components/schemas/WhatsAppNumberEventSortField' - $ref: '#/components/parameters/OrderDesc' - $ref: '#/components/parameters/PaginationLimit' - $ref: '#/components/parameters/StartingAfter' - $ref: '#/components/parameters/EndingBefore' responses: '200': description: A page of number events. content: application/json: schema: $ref: '#/components/schemas/WhatsAppNumberEventList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk /v1/whatsapp/numbers/{number_id}/profile: get: operationId: getWhatsAppNumberProfile x-snippet-key: whatsapp.numbers.profile.get summary: Get a WhatsApp number's business profile description: Returns the business profile WhatsApp shows to people this number messages, read from WhatsApp on each request rather than from a stored copy. Readable for a number your workspace connected itself and for one Bird operates on your behalf alike, since the profile is what everyone the number messages already sees; only the first can be changed. The response includes the display name WhatsApp shows for this number and the state of its review. tags: - whatsapp-numbers security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - dashboard - command parameters: - name: number_id in: path required: true description: ID of the WhatsApp number (`wan_` prefix), as returned by the number list. schema: $ref: '#/components/schemas/WhatsAppNumberID' responses: '200': description: The number's business profile. content: application/json: schema: $ref: '#/components/schemas/WhatsAppNumberProfile' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk components: schemas: WhatsAppUsernameStatus: type: string description: Where the username stands with WhatsApp. WhatsApp adds states over time, so a value outside this list can be returned. x-extensible-enum: - approved - reserved - deleted example: approved ErrorBody: type: object additionalProperties: false required: - type - code - name - message - doc_url - request_id properties: type: type: string minLength: 1 description: Broad category for coarse client branching. enum: - auth_error - bad_request_error - billing_error - conflict_error - gone_error - internal_error - misdirected_error - not_found_error - not_implemented_error - payload_too_large_error - permission_error - precondition_error - rate_limit_error - service_unavailable_error - too_early_error - validation_error code: type: string minLength: 1 pattern: ^E\d{5}$ description: Opaque, stable, unique error identifier. Never reused. name: type: string minLength: 1 description: Human-readable slug for log readability. Paired with code, never replaces it. message: type: string minLength: 1 description: Human-readable description. Not stable; clients must not parse it. param: type: string minLength: 1 description: Identifies the offending field. Omitted when not applicable. doc_url: type: string minLength: 1 format: uri description: Stable link to the docs page for this error code. request_id: type: string minLength: 1 description: Request correlation ID for support and troubleshooting. Also returned in the `X-Request-Id` response header. vendor_code: type: string minLength: 1 description: 'Verbatim error code from an external system, such as an SMTP response code or a payment decline code. Present only when the code may help you resolve the error. ' details: type: array description: Per-field validation errors. Present only on validation_error responses. items: $ref: '#/components/schemas/ErrorDetail' remediation: type: string minLength: 1 description: A human-readable next step to resolve this error. Present when a recovery is known. next: type: array description: 'The steps that resolve this error. Perform them in order, re-reading after each; a `wait` or `terminal` step is always last. Present for errors with a well-defined recovery, such as unmet preconditions and conflicts. ' items: $ref: '#/components/schemas/NextAction' WhatsAppNumberErrorCode: type: string minLength: 1 x-extensible-enum: - 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 - credit_currency_mismatch - permission_denied - invalid_request - internal_error description: 'Standardized number-connection failure: - `registration_pin_rejected`: WhatsApp refused the two-step verification PIN. - `registration_pin_rate_limited`: Too many PIN attempts occurred recently. - `registration_attempts_exhausted`: Registration is blocked for 72 hours. - `number_verification_required`: WhatsApp requires the number to be verified again. - `number_not_registered`: WhatsApp does not hold the number as registered. - `number_already_linked`: Another WhatsApp integration uses the number. - `number_already_in_use`: WhatsApp cannot accept the number. - `verification_code_not_received`: The verification text did not arrive. - `verification_rate_limited`: WhatsApp declined to send this number another verification code, having been asked too often. It clears with time; retrying sooner extends it. - `business_account_locked`: WhatsApp locked the business account. - `credit_currency_mismatch`: WhatsApp bills the business account in a currency your organization is not billed in. Connect the number under a business account WhatsApp bills in that same currency, or one WhatsApp has set no currency on: an account''s billing currency cannot be changed once WhatsApp sets it. - `permission_denied`: WhatsApp refused access to the account. - `invalid_request`: WhatsApp rejected the connection details. - `internal_error`: The service could not classify or resolve the failure. This is an open enum. Accept unrecognized values. ' example: registration_pin_rejected WhatsAppNumberList: allOf: - type: object required: - data properties: data: type: array description: The WhatsApp numbers your workspace can send from. items: $ref: '#/components/schemas/WhatsAppNumber' - $ref: '#/components/schemas/_ListEnvelope' WhatsAppNumberQualityRating: type: string minLength: 1 x-extensible-enum: - green - yellow - red - unknown description: WhatsApp quality rating for a business phone number, based on recipient feedback. `green`, `yellow`, and `red` indicate decreasing quality; sustained `red` can restrict the number. `unknown` is itself a reported rating. This is separate from a template-language quality score. Accept unrecognized values. example: green WhatsAppDisplayNameStatus: type: string description: Where WhatsApp's review of the display name stands. `available_without_review` means the name met WhatsApp's criteria for immediate use without a review step, and `none` means no display name has been submitted yet. WhatsApp adds states over time, so a value outside this list can be returned. x-extensible-enum: - approved - available_without_review - declined - expired - non_exists - pending_review - none example: approved WhatsAppNumberSortField: type: string enum: - created_at default: created_at description: Sortable fields for a WhatsApp number list. WhatsAppNumber: type: object additionalProperties: false required: - id - phone_number - name - scope - status - created_at - updated_at properties: id: allOf: - $ref: '#/components/schemas/WhatsAppNumberID' readOnly: true description: Unique identifier for the connected number. waba: type: string minLength: 1 readOnly: true description: 'The WhatsApp Business Account this number is connected under. Present only for a number your workspace connected itself. ' example: '102290129340398' phone_number: type: - string - 'null' readOnly: true description: '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. `status` is what says that. ' example: '+15550001234' number_id: allOf: - $ref: '#/components/schemas/AllocatedNumberID' readOnly: true description: '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. ' name: type: string minLength: 1 maxLength: 100 readOnly: true description: '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. ' example: Sales EU scope: allOf: - $ref: '#/components/schemas/WhatsAppNumberScope' readOnly: true description: 'Whether the number sends under a WhatsApp Business Account we operate on your behalf (`system`) or one your workspace connected itself (`workspace`). ' data_localization_region: allOf: - $ref: '#/components/schemas/WhatsAppDataLocalizationRegion' readOnly: true description: '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. ' status: allOf: - $ref: '#/components/schemas/WhatsAppNumberStatus' readOnly: true description: 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`. next: type: array readOnly: true description: '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. ' items: $ref: '#/components/schemas/NextAction' error: allOf: - $ref: '#/components/schemas/WhatsAppNumberError' readOnly: true description: 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. finish_setup_url: type: string format: uri minLength: 1 readOnly: true description: '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.' example: https://bird.com/dashboard/w/ws_01krdgeqcxet5s7t44vh8rt9mg/whatsapp/numbers?finish_setup_number=wan_01krdgeqcxet5s7t44vh8rt9mg quality_rating: allOf: - $ref: '#/components/schemas/WhatsAppNumberQualityRating' readOnly: true description: 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. messaging_limit: allOf: - $ref: '#/components/schemas/WhatsAppNumberMessagingLimit' readOnly: true description: 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. throughput_level: allOf: - $ref: '#/components/schemas/WhatsAppNumberThroughputLevel' readOnly: true description: 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. is_official_business_account: type: boolean readOnly: true description: '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.' meta_synced_at: type: string format: date-time minLength: 1 readOnly: true description: 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: type: string format: date-time minLength: 1 readOnly: true description: '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: type: string format: date-time minLength: 1 readOnly: true description: When this number was submitted for connection. updated_at: type: string format: date-time minLength: 1 readOnly: true description: When this number was last changed. WhatsAppNumberStatus: type: string minLength: 1 x-extensible-enum: - awaiting_signup - banned - connected - deleted - disconnected - failed - flagged - migrated - pending - preparing - rate_limited - restricted description: Operational state of a business phone number. The `preparing` status means the service is verifying a managed number. The `awaiting_signup` status means verification finished and you must complete signup. The `pending` status is WhatsApp's own token for a number it does not hold as registered, and is also returned when no WhatsApp status has been stored, including after setup completes. It does not by itself establish whether setup is complete. The `connected` status means registration completed. The `failed` status means connection was refused permanently. Other values are WhatsApp's own operational states for a number already connected. This is an open enum. Accept unrecognized values. example: connected AllocatedNumberID: type: string minLength: 1 pattern: ^(nda|nal)_[0-9a-hjkmnp-tv-z]{26}$ example: nda_01krdgeqcxet5s7t44vh8rt9mg description: Identifier of a number allocated to your workspace, as returned in the id field of `GET /v1/numbers`. WhatsAppNumberEventList: allOf: - type: object required: - data properties: data: type: array description: Page of number events, newest first by default. items: $ref: '#/components/schemas/WhatsAppNumberEvent' - $ref: '#/components/schemas/_ListEnvelope' WhatsAppNumberEventID: type: string minLength: 1 pattern: ^wne_[0-9a-hjkmnp-tv-z]{26}$ example: wne_01krdgeqcxet5s7t44vh8rt9mg _ListEnvelope: type: object required: - next_cursor - prev_cursor - refresh_cursor properties: next_cursor: type: - string - 'null' description: Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists. example: eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9 prev_cursor: type: - string - 'null' description: Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists. example: null refresh_cursor: type: - string - 'null' description: Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`. example: eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9 WhatsAppNumberMessagingLimit: type: string minLength: 1 x-extensible-enum: - tier_50 - tier_250 - tier_1k - tier_10k - tier_100k - tier_unlimited description: 'How many unique WhatsApp users can be messaged outside a customer service window in a rolling 24 hours, as WhatsApp''s own tier token. WhatsApp calculates this for the business portfolio, and every number in that portfolio shares it; it is not this number''s private capacity, and one number can consume all of it. Values are WhatsApp''s own tokens, lower-cased. Open enum: WhatsApp documents a 2,000 limit its published tier vocabulary has no token for, so treat an unrecognized value as a tier WhatsApp added.' example: tier_250 WhatsAppBusinessVertical: type: string description: The industry WhatsApp shows on the business profile. WhatsApp adds categories over time, so a value outside this list can be returned. x-extensible-enum: - other - auto - beauty - apparel - edu - entertain - event_plan - finance - grocery - govt - hotel - health - nonprofit - prof_services - retail - travel - restaurant - alcohol - online_gambling - physical_gambling - otc_drugs example: retail ErrorDetail: type: object additionalProperties: false required: - param - message properties: param: type: string minLength: 1 description: 'Dotted field path, such as `to[0].email`, `subject`, or `.`. When the request was rejected for a query parameter the endpoint does not declare, this carries that parameter''s name instead of a field path. ' message: type: string minLength: 1 description: What is wrong with this field. NextAction: type: object additionalProperties: false required: - kind - description properties: kind: type: string minLength: 1 x-extensible-enum: - operation - external - wait - terminal description: "What you do about this step.\n\n- `operation`: call the operation named in `operation`, then\n read again.\n- `external`: act somewhere this API does not reach, then read\n again.\n- `wait`: nothing is asked of you, so read again later.\n- `terminal`: nothing you do resolves this, so stop retrying.\n\nTolerate a value you do not recognize: show the `description` and\noffer no action.\n" description: type: string minLength: 1 description: A short, human-readable label for the step, suitable for display. operation: type: string minLength: 1 description: '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. ' params: type: object additionalProperties: type: string minLength: 1 description: '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. ' url: type: string format: uri description: '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. ' WhatsAppNumberEvent: type: object additionalProperties: false required: - id - type - summary - metadata - created_at properties: id: readOnly: true $ref: '#/components/schemas/WhatsAppNumberEventID' description: Event ID. type: type: string minLength: 1 description: 'Type of number event. `whatsapp_number.messaging_limit_updated` and `whatsapp_number.profile_name_update` are reported by WhatsApp as they happen; `whatsapp_number.quality_rating_updated` is observed when Bird next reads the number, so it can lag the change by up to an hour. `whatsapp_number.status_changed` records every move of the `status` field on the number itself, whichever side caused it. Open enum: new event types may be added over time, so treat any unrecognized value as a future event rather than an error. The values below are the types known at this version.' x-extensible-enum: - whatsapp_number.created - whatsapp_number.messaging_limit_updated - whatsapp_number.profile_name_update - whatsapp_number.quality_rating_updated - whatsapp_number.status_changed example: whatsapp_number.quality_rating_updated summary: type: string minLength: 1 description: Human-readable summary of what changed. example: Quality rating dropped to medium. metadata: type: object description: Structured details for the event. `from` and `to` carry the values that changed, and `from` is absent when the number had no prior value to report. A status change into `failed` also carries the `reason`; a display-name decision carries `new_display_name`, `decision`, and, when WhatsApp named one for a rejection, `rejection_reason`. A messaging-limit change also carries the `trigger` WhatsApp named for it, such as `onboarding` or `throughput_upgrade`, when it named one. A `whatsapp_number.created` event carries the `source` the number came from, and its `phone_number` once one is known. additionalProperties: true created_at: type: string format: date-time minLength: 1 description: When the event was recorded. WhatsAppNumberError: type: object additionalProperties: false readOnly: true required: - code description: Why a number's connection was refused for good. `code` is the standardized reason. `description` explains the failure where one was recorded, and `meta_error_code` carries WhatsApp's own code when available. It accompanies the `failed` status only; a number still being retried carries no error. properties: code: allOf: - $ref: '#/components/schemas/WhatsAppNumberErrorCode' readOnly: true description: Standardized failure reason. description: type: string minLength: 1 readOnly: true description: '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.' example: 'Cannot Create Certificate: Please ensure two-factor authentication is disabled.' meta_error_code: type: - string - 'null' readOnly: true description: '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.' example: '2388001' WhatsAppNumberScope: type: string minLength: 1 enum: - system - workspace example: workspace Error: type: object additionalProperties: false required: - error properties: error: $ref: '#/components/schemas/ErrorBody' WhatsAppNumberThroughputLevel: type: string minLength: 1 x-extensible-enum: - standard description: How fast WhatsApp lets this number send, as WhatsApp's own level token. `standard` is 80 messages per second; WhatsApp upgrades an eligible number to 1,000 per second automatically. Values are WhatsApp's own tokens, lower-cased. This enum is open because WhatsApp publishes no vocabulary for the field. The `standard` value is the only value Bird has measured. The upgraded level's token remains unknown until a number returns it. example: standard WhatsAppNumberProfile: type: object additionalProperties: false description: The business profile WhatsApp shows to people this number messages. It is read from WhatsApp on each request rather than from a stored copy, so it is always current and a WhatsApp outage makes it briefly unavailable. properties: display_name: type: string readOnly: true description: 'The name WhatsApp verifies for this number. Once WhatsApp approves it, it appears at the top of a chat with this number; `display_name_status` is what says whether it has. Set when the number was connected, and changed from the dashboard or the CLI, as [WhatsApp phone numbers](/docs/guides/whatsapp/phone-number-setup) explains. This field still returns the current name until a requested change completes. ' example: Lucky Shrub display_name_status: allOf: - $ref: '#/components/schemas/WhatsAppDisplayNameStatus' readOnly: true description: 'Where WhatsApp''s review of the display name stands. A name still under review is not yet shown at the top of a chat. ' new_display_name: type: string readOnly: true description: 'The display name whose change has been requested, whether or not WhatsApp is reviewing it. Absent when no change is pending. ' example: Lucky Shrub Garden Center new_display_name_status: allOf: - $ref: '#/components/schemas/WhatsAppDisplayNameStatus' readOnly: true description: 'Where the requested display name stands with WhatsApp, including whether it is being reviewed or was accepted for immediate use without a review. Absent when no change is pending. If WhatsApp accepts the name it becomes `display_name`. Every other outcome leaves the number on the name it already had: `declined` is WhatsApp refusing the name, and `expired` is a request that no longer stands and has to be made again. ' username: type: string readOnly: true description: 'The username WhatsApp users can find this number by, without an `@`. Absent when the number has no username. Once set it cannot be removed through this API. ' example: goldcrest.support username_status: allOf: - $ref: '#/components/schemas/WhatsAppUsernameStatus' readOnly: true description: 'Where the username stands with WhatsApp. Absent when the number has no username. ' about: type: string maxLength: 139 description: The short line shown under the business name in a chat. example: Open Monday to Friday, 9am to 6pm CET. address: type: string maxLength: 256 description: The business address shown on the profile. example: Trompenburgstraat 2C, 1079 TX Amsterdam description: type: string maxLength: 256 description: The longer description shown on the profile. example: Bird is the platform for messaging with your customers. email: type: string format: email maxLength: 128 description: The contact email shown on the profile. example: hello@bird.com vertical: allOf: - $ref: '#/components/schemas/WhatsAppBusinessVertical' description: The industry WhatsApp shows on the profile. websites: type: array maxItems: 2 items: type: string maxLength: 256 description: Up to two websites shown on the profile. example: - https://bird.com profile_picture_url: type: string readOnly: true description: A link to the profile picture WhatsApp currently shows. WhatsApp signs this link and it expires within days, so load it when you display it and never store it. It is served with permissive cross-origin headers, so a browser can load it directly. example: https://pps.whatsapp.net/v/t61.24694-24/643148303_1005107588793925.jpg SortOrder: type: string enum: - asc - desc description: Sort direction, ascending or descending. WhatsAppDataLocalizationRegion: type: string minLength: 2 enum: - AU - ID - IN - JP - SG - KR - DE - CH - GB - BR - BH - ZA - AE - CA description: 'A country where WhatsApp can store a business phone number''s message content at rest, as its two-letter ISO 3166 code. ' example: DE WhatsAppNumberID: type: string minLength: 1 pattern: ^wan_[0-9a-hjkmnp-tv-z]{26}$ example: wan_01krdgeqcxet5s7t44vh8rt9mg WhatsAppNumberEventSortField: type: string enum: - created_at default: created_at description: Sortable fields for a WhatsApp number's event list. responses: InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' RateLimited: description: Rate limit exceeded headers: RateLimit: $ref: '#/components/headers/RateLimit' RateLimit-Policy: $ref: '#/components/headers/RateLimit-Policy' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' ServiceUnavailable: description: 'The service is temporarily unavailable. If `Retry-After` is present, wait for that delay before retrying; otherwise, retry with exponential backoff. Reuse the same idempotency key and request when retrying a mutation. ' headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Authentication required content: application/json: schema: $ref: '#/components/schemas/Error' Unprocessable: description: 'The request has invalid field values, violates a business rule, or carries a query parameter the endpoint does not declare. Field validation errors use `type: validation_error` and include the affected fields in `details`. Business-rule errors identify the failed rule in `type`. ' content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' Conflict: description: Resource conflict content: application/json: schema: $ref: '#/components/schemas/Error' headers: RetryAfter: description: 'Number of seconds to wait before retrying the request. ' schema: type: integer minimum: 0 example: 35 RateLimit: description: 'Remaining capacity for the request''s rate-limit policy as an IETF Structured Field. Format: `"";r=;t=`. ' schema: type: string example: '"email_send";r=842;t=35' RateLimit-Policy: description: 'Effective quota for the request''s rate-limit policy as an IETF Structured Field. Format: `"";q=;w=`. ' schema: type: string example: '"email_send";q=1000;w=60' parameters: StartingAfter: name: starting_after in: query required: false description: Cursor from the `next_cursor` field of a previous list response. Returns items immediately after the cursor position in the current sort order. schema: type: string EndingBefore: name: ending_before in: query required: false description: Cursor from the `prev_cursor` or `refresh_cursor` field of a previous list response. Returns items immediately before the cursor position in the current sort order. `prev_cursor` returns the preceding page. `refresh_cursor` anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since. schema: type: string PaginationLimit: name: limit in: query required: false description: Maximum number of items to return per page. schema: type: integer minimum: 1 maximum: 100 default: 25 OrderDesc: name: order in: query required: false description: 'Sort direction. Defaults to `desc`, which sorts from newest to oldest or largest to smallest, depending on the selected sort field. ' schema: $ref: '#/components/schemas/SortOrder' default: desc securitySchemes: BearerAuth: type: http scheme: bearer description: 'Pass the API key as a bearer token in the `Authorization` header. Keys use the format `bk_{region}_*`. The prefix identifies the region and selects the API endpoint. Official Bird SDKs and the CLI derive the region from the key. ' CookieAuth: type: apiKey in: cookie name: bird_session description: 'Session cookie set after signing in to the Bird dashboard. The cookie value is an opaque session token; no session data is stored in the cookie itself. ' RealtimeKey: type: apiKey in: header name: X-Realtime-Key description: 'The Realtime app key. Together with `X-Realtime-Secret`, it authenticates a request to the Realtime API in addition to the workspace credential. Both values come from the app''s credentials and must belong to the calling workspace. Official Bird SDKs accept the pair as client configuration. ' RealtimeSecret: type: apiKey in: header name: X-Realtime-Secret description: 'The Realtime app secret paired with `X-Realtime-Key`. The API returns the secret only when the key is created and does not store it. Create a new key and revoke the current key if you lose the secret. Official Bird SDKs accept the pair as client configuration. '