Sign inGet Started

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 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. An API key cannot read organization billing.

The examples read your key from BIRD_API_KEY, its regional host 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 and GET /v1/organization/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. 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:

Code example
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 or GET /v1/sms/senders/{sender_id}. Create one with POST /v1/sms/senders if needed.
  2. Read GET /v1/sms/senders/{sender_id}/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, then call POST /v1/sms/senders/{sender_id}/registrations 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, with sms_sender as subject_kind and the registration as subject_id:

Code example
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 explains each one, and Upload each document covers the files a document field asks for. File every answer with POST /v1/compliance/submissions, naming the registration as the subject:

Code example
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 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}, 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, as Correct a rejected answer shows.

Next steps

Continue with the documentation, guides and examples for this topic.