Send SMS
One API for every text you send.
Send transactional messages and notifications through Bird. Supply your text and sender, or use a template; inspect the encoding and segment count in the response. Add an idempotency key for safe retries and follow delivery through signed webhooks.
One message. A visible outcome.
Example send
Explore acceptance and a later carrier receipt. This example does not send a text; delivery does not establish that someone read it.
Trusted every day by teams that build world-class software
Read more customer storiesTest your first SMS integration.
From the language you already use.
Sending is the core of the Bird SMS API. The example below shows the request shape. For a controlled test, replace the recipient with the documented sandbox number +15005550006. Set up a suitable US sender and enable the destination first, then verify acceptance and delivery events before sending to customers.
const msg = await bird.sms.send({
from: "+15557654321",
to: "+14155550100",
text: "Your verification code is 123456.",
category: "authentication",
});
console.log(msg.id, msg.status);An SMS send delivers the text you supply. For login and account verification, use Bird Verify to generate, expire and check codes as part of a verification flow.
Build on a clear sending contract.
Prepare the request and follow the result.
- 01
Segment counting before send.
Bird reports the calculated encoding and segment count in its response. Use the segment calculator to inspect a draft before you submit it.
- 02
GSM-7 and Unicode, decided for you.
Characters determine the encoding. GSM-7 fits 160 units in a single segment; Unicode fits 70. Multipart messages reserve space for reassembly, and emoji can occupy more than one unit.
- 03
Batch in one call.
Submit up to 100 independent messages in one batch. Validation happens before enqueueing; each accepted message then has its own outcome.
- 04
Retry with an idempotency key.
Use one idempotency key per logical request and reuse it for an identical retry. The retained API response can be replayed; this does not guarantee exactly-once carrier delivery.
- 05
Delivery events for your application.
Subscribe to accepted, sent and terminal outcome events. Verify signatures, deduplicate webhook retries and use message reads to investigate missing or delayed observations.
Move the integration with a controlled test.
Map your current request fields, sender registrations and event handling to Bird. Reconcile opt-outs before moving traffic, then compare a controlled test before changing production routing.
import twilio from "twilio";
const client = twilio(accountSid, authToken);
await client.messages.create({
from: "+14155550172",
to: "+15005550006",
body: "Your code is 123456.",
});import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });
await bird.sms.send({
from: "+14155550172",
to: "+15005550006",
text: "Your code is 123456.",
category: "authentication",
});Know the segment count before submission.
GSM-7 fits 160 septets in a single segment; UCS-2 fits 70 code units. Multipart capacity is 153 or 67 respectively. Extended GSM-7 characters use two septets and emoji may use two code units. Bird returns encoding and segments at acceptance; the applicable rate and any carrier fee are priced separately.
const { data, error } = await bird.sms.send({
from: "Bird",
to: "+31612345678",
text: "Your code is 123456.",
category: "authentication",
}).safe();
if (error) throw error;
console.log(data.segments);
// → { characters: 20, count: 1, encoding: "GSM_7BIT" }
One message or a hundred, one call.
Batch up to 100 independent messages, each with its own recipient and text. Invalid input rejects the request before enqueueing. After a successful 202 response, processing and delivery can succeed or fail separately for each SMS. Reuse the request and idempotency key when retrying within the documented retention window.
const { data: batch, error } = await bird.sms
.sendBatch(
users.map((u) => ({
from: "Bird",
to: u.phone,
text: `Hi ${u.name}, your appointment is tomorrow at ${u.time}.`,
})),
{ idempotencyKey: `reminders-${runId}` },
)
.safe();
if (error) throw error;
console.log(`queued ${batch.data.length} messages`);Follow acceptance through to the reported outcome.
A successful request returns 202 Accepted. Charging and carrier submission happen later and can still fail. Consume signed delivery events and inspect the message record when investigating the outcome.
import { bird } from "@/lib/bird";
export async function POST(req: Request) {
const event = bird.webhooks.unwrap(
await req.text(),
Object.fromEntries(req.headers),
);
switch (event.type) {
case "sms.delivered":
await markDelivered(event.data.sms_id);
break;
case "sms.failed":
await flag(event.data.to, event.data.error?.description);
break;
}
return new Response(null, { status: 204 });
}Inspect failures by their reported reason. Supported STOP keywords and carrier opt-outs create suppressions; other delivery failures do not automatically become an opt-out.
sms.acceptedAccepted by the API and queued for the carrier hand-off.sms.sentSubmitted to the destination carrier's SMSC.sms.deliveredDelivery receipt received from the carrier (DLR).sms.failedA terminal failure for this SMS attempt. Inspect the reported error and the message timeline.
Go deeper in the docs.
Wire up webhooks, make every send safe to retry with idempotency keys, and read the error reference so you handle each failure the right way.
Put it into practice.
Continue with the documentation, guides and examples for this topic. Resources are in English.
Questions before you build
Do I choose the sender?
How do retries avoid a duplicate text?
Does accepted mean delivered?
Is a batch the same as a broadcast?
The rest of the SMS platform
One API, one set of keys. Explore the other capabilities.
Build the complete messaging workflow.
Connect SMS sending to the sender, destination and delivery controls your application needs. Prepare the integration before sending to customers.