---
title: "Register an SMS sender over the API"
description: "Claim an SMS sender, create its registration for a country, check the price and wallet funds first, and file the registration's paperwork with the API."
canonical: "https://bird.com/docs/guides/sms/sender-registration-api"
---

# Register an SMS sender over the API

Some countries only deliver SMS from a sender that is registered for them. Over the API you claim the sender, create its registration for the destination country, and file the paperwork that registration asks for, the same steps the dashboard takes. A registration can charge a setup fee as soon as it is created, so check the price and your wallet first.

## Before you start

- An [API key](/docs/guides/authentication) with `sms_management` at write level to create senders and registrations, and `compliance` at write level to file their paperwork.
- To check prices and wallet funds first, a delegated user OAuth token, described in [Check registration prices and wallet funds](#check-registration-prices-and-wallet-funds). An API key cannot read organization billing.

The examples read your key from `BIRD_API_KEY`, its [regional host](/docs/api/regions) from `BIRD_API_URL`, the destination country from `COUNTRY_CODE`, and the registration's `scr_` identifier from `REGISTRATION_ID` once you have it.

## Check registration prices and wallet funds

To show the same price and wallet information as the dashboard, use [`GET /v1/organization/billing/prices/resolve`](/docs/api/reference/get-billing-resolved-price) and [`GET /v1/organization/billing/wallets`](/docs/api/reference/list-billing-wallets). These reads require a delegated user OAuth token with `org:billing:read`. Workspace API keys, organization master keys, and service-account tokens cannot grant this organization billing permission.

Use OAuth protected-resource discovery and authorization-server metadata to obtain the `issuer`, `registration_endpoint`, `authorization_endpoint`, and `token_endpoint`, as described in [OAuth discovery](/docs/ai/mcp-server#how-the-handshake-works). Register your own public OAuth client at the discovered `registration_endpoint`, providing `client_name`, `redirect_uris`, and `token_endpoint_auth_method: "none"`. Send the user to the discovered `authorization_endpoint` with that `client_id`, `response_type=code`, your registered `redirect_uri`, a fresh `state`, `scope=org:billing:read`, a `code_challenge` derived from your verifier, and `code_challenge_method=S256`. On the consent screen, the user selects a workspace in the intended organization and grants `org:billing:read` where their role permits it. Validate `state` against the value you sent and `iss` against the issuer from discovery, then exchange the code at the discovered `token_endpoint` with `grant_type=authorization_code`, the returned `code`, `client_id`, the same `redirect_uri`, and the original `code_verifier`. Use the resulting access token as `BIRD_OAUTH_TOKEN`. Honor its `expires_in`; securely store the refresh token and replace it after each successful refresh. Serialize refresh attempts because each refresh token is single-use. Reauthorize when the refresh grant expires or is revoked.

The token is bound to the selected workspace and its organization; billing reads apply to that organization. `X-Organization-Id` cannot switch it to another organization; repeat authorization for that organization. Set the regional host explicitly when configuring an SDK with this token.

For an alphanumeric SMS sender, resolve its registration setup price using the destination country:

```bash
curl --get "$BIRD_API_URL/v1/organization/billing/prices/resolve" \
  --header "Authorization: Bearer $BIRD_OAUTH_TOKEN" \
  --data-urlencode 'product_slug=sms_sender_registration' \
  --data-urlencode "dimensions[country_code]=$COUNTRY_CODE" \
  --data-urlencode 'dimensions[sender_type]=alphanumeric'

curl "$BIRD_API_URL/v1/organization/billing/wallets" \
  --header "Authorization: Bearer $BIRD_OAUTH_TOKEN"
```

Resolve `product_slug=sms_sender` with the same dimensions for the recurring price. For the same balance check as the dashboard, add the setup response's `gross_price.amount` to the first recurring charge: the recurring response's `gross_price.amount` multiplied by its `intervals_per_charge`. Compare that total with `balance` on the wallet in `data` whose `currency_code` matches both prices' `gross_price.currency_code`. Use decimal arithmetic without rounding the returned amounts. If the total exceeds the balance, the difference is the shortfall; top up or check your organization's credit arrangement before creating the registration. The server also considers any credit limit in its final affordability check. These reads show current prices and funds; they do not reserve funds or approve a registration.

## Get the registration to file for

1. Find the sender with [`GET /v1/sms/senders`](/docs/api/reference/list-workspace-sms-senders) or [`GET /v1/sms/senders/{sender_id}`](/docs/api/reference/get-workspace-sms-sender). Create one with [`POST /v1/sms/senders`](/docs/api/reference/create-workspace-sms-sender) if needed.
2. Read [`GET /v1/sms/senders/{sender_id}/requirements`](/docs/api/reference/list-workspace-sms-sender-requirements) for the destination. Follow its `next` steps. If it asks you to create a registration, check its price as in [Check registration prices and wallet funds](#check-registration-prices-and-wallet-funds), then call [`POST /v1/sms/senders/{sender_id}/registrations`](/docs/api/reference/create-workspace-sms-sender-registration) with `country_code`.
3. Read the sender requirements again and keep the row's `registration_id` (`scr_`) as `REGISTRATION_ID` and its `program` as `PROGRAM`. File paperwork only when the row's `has_compliance_requirements` is `true`; when it is `false`, the registration needs no submission or a dedicated program owns it, so follow the row's `next` instead. Dedicated programs such as toll-free verification and 10DLC have their own steps.

Sender reads need `sms_management:read`; sender and registration creation need `sms_management:write`. Filing their paperwork also needs `compliance:write`.

## File the registration's paperwork

This section applies to a registration whose `has_compliance_requirements` is `true`.

Read what the registration asks for with [`GET /v1/compliance/requirements`](/docs/api/reference/get-workspace-compliance-requirements), with `sms_sender` as `subject_kind` and the registration as `subject_id`:

```bash
curl "$BIRD_API_URL/v1/compliance/requirements?subject_kind=sms_sender&subject_id=$REGISTRATION_ID&country_code=$COUNTRY_CODE&program=$PROGRAM" \
  --header "Authorization: Bearer $BIRD_API_KEY"
```

The fields read as they do for a number; [Read what the country asks for](/docs/guides/numbers/ownership-paperwork#read-what-the-country-asks-for) explains each one, and [Upload each document](/docs/guides/numbers/ownership-paperwork#upload-each-document) covers the files a `document` field asks for. File every answer with [`POST /v1/compliance/submissions`](/docs/api/reference/create-workspace-compliance-submission), naming the registration as the subject:

```bash
curl --request POST "$BIRD_API_URL/v1/compliance/submissions" \
  --header "Authorization: Bearer $BIRD_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: sender-$REGISTRATION_ID" \
  --data '{
    "subject_kind": "sms_sender",
    "subject_id": "'"$REGISTRATION_ID"'",
    "program": "'"$PROGRAM"'",
    "country_code": "'"$COUNTRY_CODE"'",
    "answers": [{ "key": "legal_entity_name", "value": "Goldcrest Communications Inc" }]
  }'
```

The sample shows one answer; send one entry for every field whose `required` is `true`, and for each optional field you have a real answer for, as [Submit every answer](/docs/guides/numbers/ownership-paperwork#submit-every-answer) explains. A `201` response returns the submission; keep its `csb_` `id`. A sender has no `ownership` field, so follow the review by reading the submission with [`GET /v1/compliance/submissions/{submission_id}`](/docs/api/reference/get-workspace-compliance-submission), whose answers carry their `state` and, when rejected, a `reason`, or by reading the sender requirements again for the registration's status. Replace rejected answers with [`POST /v1/compliance/submissions/{submission_id}/resupply`](/docs/api/reference/update-workspace-compliance-submission), as [Correct a rejected answer](/docs/guides/numbers/ownership-paperwork#correct-a-rejected-answer) shows.

## Next steps

- [File a number's ownership paperwork](/docs/guides/numbers/ownership-paperwork)
- [SMS sender IDs](/docs/guides/sms/senders)

## Related resources

- [Sending your first SMS](/learn/sms/sending-your-first-sms) (video)
- [What does SMS mean?](/explained/sms/what-does-sms-mean) (answer)
- [SMS](/sms-api) (product)
- [Build your first integration](/learn/paths/integration) (course)

[Get an implementation brief](/learn/workspace?topic=sms)
