Send your first SMS
Send a text message to your own phone with Bird SMS, then read the message back to see whether it was delivered. This quickstart uses a built-in template, which supplies the text, the category, and a shared sender that Bird selects for the destination. You do not need a sender ID or a sender registration for it.
Before you start, make sure your organization's wallet has funds. SMS sends draw on the wallet, and Bird refuses a send the balance cannot cover with 402 WalletInsufficientBalance. Payment methods and wallet covers topping up.
1. Create an API key
In the dashboard, go to Platform tools > API keys and create a key with the sms:write scope, which covers sending and reading messages. Keys are scoped to a region and look like bk_us1_... or bk_eu1_.... The region in the prefix tells you which API host to call: https://us1.platform.bird.com or https://eu1.platform.bird.com.

The full key is shown once, at creation time. Copy it somewhere safe, then export it for the cURL examples:
export BIRD_API_KEY="bk_us1_..."2. Enable the destination country
Bird sends SMS only to the countries enabled for your workspace. A send to any other country fails with 422 SMSDestinationNotEnabled. Enable the country of your phone number under SMS > Destinations. If it already shows as enabled, continue to step 3.
From a terminal, the Bird CLI makes the same change. Pass the country's two-letter ISO code, for example US for the United States. If your CLI login lacks access to SMS settings, the command prints the bird auth login command that adds it:
bird sms destinations update --destination US=trueAgents connected to the MCP server use the sms_destinations_update tool. The public API has no operation for destinations. A change can take up to a minute to apply to sends.
3. Send the message
Send the built-in bird_otp_verification template to your phone. It renders as "493021 is your verification code. Do not share it." with the code value you pass. Install the Bird SDK for your language by following its SDK quickstart.
In the SDK tabs, replace the example API key, and replace +14155550100 with your mobile number in E.164 format. The CLI uses your login, and the cURL tab uses BIRD_API_KEY.
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: "bk_XXXXXXXXXXXXXXXXXXXXXXXX" });
const msg = await bird.sms.send({
to: "+14155550100",
template: { slug: "bird_otp_verification", parameters: { code: "493021" } },
});
console.log(msg.id, msg.status);from bird import APIError, Bird
with Bird(api_key="bk_XXXXXXXXXXXXXXXXXXXXXXXX") as client:
try:
message = client.sms.send(
to="+14155550100",
template="bird_otp_verification",
parameters={"code": "493021"},
)
print(message.id, message.status)
except APIError as err:
print("send failed:", err)package main
import (
"context"
"fmt"
"log"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey("bk_XXXXXXXXXXXXXXXXXXXXXXXX"))
if err != nil {
log.Fatal(err)
}
msg, err := client.Sms.Send(context.Background(), bird.SmsSendParams{
To: "+14155550100",
Template: "bird_otp_verification",
Parameters: map[string]any{"code": "493021"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use MessageBird\Bird;
$bird = new Bird('bk_XXXXXXXXXXXXXXXXXXXXXXXX');
$message = $bird->sms->send(
to: '+14155550100',
template: 'bird_otp_verification',
parameters: ['code' => '493021'],
);
echo $message->getId(), ' ', $message->getStatus(), "\n";bird sms send \
--parameters '{"code":"493021"}' \
--template bird_otp_verification \
--to +14155550100curl -X POST https://us1.platform.bird.com/v1/sms/messages \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155550100",
"template": {
"slug": "bird_otp_verification",
"parameters": { "code": "493021" }
}
}'If your key starts with bk_eu1_, call https://eu1.platform.bird.com instead.
The API responds with 202 Accepted and the message. Its id starts with sms_, and its status is accepted: Bird has the message and delivers it asynchronously. Keep the id for the next step. The message arrives from the shared sender Bird selected for your country.
4. Check the delivery status
Fetch the message by its ID. A read right after the send can return 404 until the message becomes visible on the read endpoint, which happens shortly after the 202. Read it again a moment later. Replace SMS_MESSAGE_ID with the id from step 3, and the example API key in the SDK tabs with your own. The Go SDK has no typed method for reading an SMS message, so the Go tab calls the API path through the SDK's client.Get request method.
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: "bk_XXXXXXXXXXXXXXXXXXXXXXXX" });
const msg = await bird.sms.get("SMS_MESSAGE_ID");
console.log(msg.id, msg.status);from bird import APIError, Bird
with Bird(api_key="bk_XXXXXXXXXXXXXXXXXXXXXXXX") as client:
try:
message = client.sms.get("SMS_MESSAGE_ID")
print(message.id, message.status)
except APIError as err:
print("read failed:", err)package main
import (
"context"
"fmt"
"log"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey("bk_XXXXXXXXXXXXXXXXXXXXXXXX"))
if err != nil {
log.Fatal(err)
}
var msg bird.SMSMessage
if err := client.Get(context.Background(), "/v1/sms/messages/SMS_MESSAGE_ID", &msg); err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use MessageBird\Bird;
$bird = new Bird('bk_XXXXXXXXXXXXXXXXXXXXXXXX');
$message = $bird->sms->get('SMS_MESSAGE_ID');
echo $message->getId(), ' ', $message->getStatus(), "\n";bird sms get SMS_MESSAGE_IDcurl https://us1.platform.bird.com/v1/sms/messages/SMS_MESSAGE_ID \
-H "Authorization: Bearer $BIRD_API_KEY"The status field reports where the message is:
accepted: Bird has the message and has not yet handed it to a carrier.sent: the carrier has the message, andsent_atrecords when Bird handed it over.delivered: the carrier confirmed delivery, anddelivered_atrecords when.undelivered,failed,rejected, orexpired: the message did not reach the phone.last_errorgives the reason, and Delivery errors explains each one.
Poll until the status leaves accepted and sent, or subscribe to the SMS events to receive each change by webhook. Every message also appears on the Messages page with its event timeline.
Fix a failed send
422SMSDestinationNotEnabled: the recipient's country is not enabled for your workspace. Enable it as in step 2, wait up to a minute, and send again.402WalletInsufficientBalance: the wallet cannot cover the message. Top up the wallet, then send again.403InsufficientScope: the API key lacks thesmsscope. Edit the key's scopes or create a key withsms:write.
Next steps
- Sending SMS: send your own text with a sender and category, in batches, and with safe retries.
- SMS sender IDs: choose a sender for each country and register it where the country requires it.
- SMS templates: the built-in template catalog and its variables.
- SMS events: the event types and webhook delivery for every status change.
- SMS API reference: the full request and response schema.
Related resources
Continue with the documentation, guides and examples for this topic.