Reply to incoming SMS from your application
Answer HOURS with the sample studio's opening times. Your application owns that response. Bird supplies the inbound event and the outbound send operation.
1. Receive a real inbound message
Start with the delivery-receipt receiver. Subscribe it to sms.received and use an owned number that supports inbound SMS in its destination. Alphanumeric sender IDs cannot receive a handset reply.
From a phone you control, send HOURS to that number. Confirm that the receiver saved sms.received. Inbound data.to is your number; data.from is the subscriber. They exchange roles in the reply. See the event contract.
2. Process a bounded reply job
Set BIRD_SMS_FROM to that owned number and BIRD_API_KEY to a key with SMS sending permission. Save this as reply_worker.py beside receiver.py, then run python reply_worker.py once.
The example processes stored HOURS events. Its durable replies table claims an inbound message once, so an event replay or a second worker invocation does not automatically send another reply.
Codevoorbeeld
import json
import logging
import os
from uuid import NAMESPACE_URL, uuid5
from bird import Bird, APIError
from receiver import database
# Sample business content belongs to this application.
REPLY = "Our demo studio is open Monday to Friday, 09:00 to 17:00."
owned_number = os.environ["BIRD_SMS_FROM"]
db = database()
db.execute("""CREATE TABLE IF NOT EXISTS replies (
inbound_id TEXT PRIMARY KEY, request_key TEXT, recipient TEXT, body TEXT,
state TEXT, message_id TEXT)""")
db.commit()
try:
for (raw,) in db.execute("SELECT body FROM events").fetchall():
event = json.loads(raw)
if event["type"] != "sms.received":
continue
data = event["data"]
if data["to"] != owned_number or data.get("text", "").strip().upper() != "HOURS":
continue
inbound_id = data["workspace_id"] + ":" + data["sms_id"]
request_key = str(uuid5(NAMESPACE_URL, "bird-sms-hours:" + inbound_id))
with db:
added = db.execute("INSERT OR IGNORE INTO replies VALUES (?, ?, ?, ?, 'pending', NULL)",
(inbound_id, request_key, data["from"], REPLY)).rowcount
if not added:
continue
with db:
db.execute("UPDATE replies SET state='sending' WHERE inbound_id=?", (inbound_id,))
try:
with Bird(api_key=os.environ["BIRD_API_KEY"]) as bird:
message = bird.sms.send(
from_=owned_number, to=data["from"], text=REPLY,
category="service", options={"idempotency_key": request_key},
)
except APIError:
logging.exception("SMS reply failed; reconcile the saved attempt")
with db:
db.execute("UPDATE replies SET state='review' WHERE inbound_id=?", (inbound_id,))
continue
with db:
db.execute("UPDATE replies SET state='accepted', message_id=? WHERE inbound_id=?",
(message.id, inbound_id))
finally:
db.close()Only HOURS triggers this sample. Other messages remain stored for another application handler. In particular, the worker never replies to STOP; Bird's configured keyword and opt-out handling remains responsible for its supported keywords. This is not a complete multilingual consent handler.
3. Inspect and recover
Read the replies table and the outbound message's delivery receipt. accepted in this table means the API accepted the reply, not that the phone received it.
| Worker record | What to do |
|---|---|
| accepted with a message ID | Read that message or consume its delivery events. |
| review | Inspect the error and original send in Bird before retrying. |
| sending after a worker crash | Reconcile the uncertain attempt; do not treat it as unsent. |
| pending after interruption | Inspect whether sending began before resuming the saved operation. |
The worker deliberately leaves interrupted or failed attempts visible. A production worker needs an operator or scheduled reconciliation path that retains the original recipient, content and request key. Follow idempotency retention and recovery; a new key or a retry beyond retained responses can create another send.
For high volume, claim bounded batches of unprocessed events in a shared database instead of scanning the local demonstration table. Keep the business action, deduplication record and pending reply together. Apply your retention and erasure policy to the event and reply records.
Continue the integration
- Two-way SMS: customer-service use cases and setup.
- Sender requirements: inbound-capable numbers and destination rules.
- SMS framework examples and SMS resources.
Gerelateerde bronnen
Ga verder met de documentatie, gidsen en voorbeelden voor dit onderwerp. De bronnen zijn in het Engels.
Bekijk de gidsSending your first SMSBegrijp het conceptWhat does SMS mean?Ontdek de mogelijkheidSMSVolg het leerpadBuild your first integration
Probeer de oefening en ontvang een implementatieoverzicht