Every message someone sends to your provisioned number arrives as an HMAC-signed webhook. Read the text, reply from the same number, and let Bird handle STOP and HELP for you. Build conversational flows and auto-replies on the API you already send with.
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });
const code = generateOtp();
const { data, error } = await bird.sms.send({
from: "Bird",
to: "+15005550006",
text: `Your Bird verification code is ${code}. Reply STOP to opt out.`,
}).safe();
if (error) throw error;
console.log(data.id);
// → "sms_4kT01Lq2m..."Today at 2:14 PM
Inbound is just another webhook.
Two-way is part of the Bird SMS API. When someone texts your number, you get an sms.received event on the same signed, replay-protected endpoint that already carries your delivery receipts. There's no second integration and no polling — verify the signature once and switch on the type.
Listen for what comes back.
An inbound message and an opt-out are events, same as a delivery receipt. Verify one signature, switch on the type, and handle each one in the handler you already wrote.
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.received":
await handleInbound(event.data.from, event.data.text);
break;
case "sms.opted_out":
await removeFromCampaigns(event.data.from);
break;
}
return new Response(null, { status: 204 });
}The payload is the same envelope on every Bird channel: an evt_ id, an HMAC signature, and a replay-protected timestamp.
sms.receivedAn inbound message landed on your number — carries the sender, your number, and the text.sms.deliveredA reply you sent reached the handset (carrier DLR).sms.opted_outThe sender texted STOP — Bird suppressed them and blocks future sends.
A signed inbound message, in full.
Here's what an sms.received event looks like on the wire. The from is whoever texted you, the to is your provisioned number, and segments and encoding are reported the same way they are on a send, so a long inbound reply is never a surprise.
{
"id": "evt_7nQ9xLp2aR...",
"type": "sms.received",
"created_at": "2026-06-26T14:03:11Z",
"data": {
"id": "sms_5hV02Mr3n...",
"from": "+15005550006",
"to": "+14155550172",
"text": "YES book me in for Thursday",
"encoding": "GSM-7",
"segments": 1
}
}Reply from the same number.
A reply is a send with from and to swapped. Set from to your number and to to the original sender, and the conversation stays on one number, so the recipient sees a thread instead of a new sender each time. Build auto-replies, confirmations, or a full conversational flow on top.
async function handleInbound(from: string, text: string) {
if (/^yes\b/i.test(text)) {
const { error } = await bird.sms.send({
from: "+14155550172", // your two-way number
to: from, // reply to the sender
text: "Booked. See you Thursday at 10am.",
}).safe();
if (error) throw error;
}
}STOP, HELP, and START are handled for you.
Bird recognizes the reserved keywords before they reach your handler: STOP adds the sender to your suppression list and fires sms.opted_out, HELP returns an automatic help reply, and START opts them back in. Later sends to a suppressed number are blocked automatically. You can still keep your own keywords for YES, BOOK, or anything your flow needs. The full keyword and opt-out rules live in opt-out handling.
Two things you'll want next.
Inbound needs a two-way-capable number — long codes, short codes, and toll-free can receive, alphanumeric sender IDs can't. For richer back-and-forth on the same handset (typing indicators, read receipts, carousels), RCS upgrades the conversation where the device supports it.
Go deeper in the docs.
Wire up webhooks for inbound events, read the error reference for the failures you'll handle, and check abuse and compliance for the keyword and consent rules.
Two-way SMS FAQ
How does an inbound SMS reach my app?+
Can I reply to an inbound message?+
What happens when someone texts STOP?+
Do I need a special number for two-way?+
The rest of the SMS platform
One API, one set of keys. Explore the other capabilities.
Send and receive on one number, one API.
Two-way inbound is one capability of the Bird SMS API: sending, numbers, compliance, routing, and analytics ship with it, on infrastructure we've run for a decade.