Transactional notifications
Twilio SMS on accept, reject, timeout, and ready. E-mail stays unused (no customer_email on orders).
Transactional notifications
Slice 13 sends transactional SMS (Twilio, ADR-008) after four successful order writes: Accept, Reject, Timeout, and Ready. Checkout does not send. There is no dashboard or storefront send button.
Slice 10 wired the clients. This slice attaches sendSms to those four events only. sendEmail is not called: orders has customer_phone and no customer_email. Do not add that column.
Events that send
After a successful order write (same tenant, no 404/409):
| Event | When | SMS body text (prefixed with {restaurantName}: ) |
|---|---|---|
| Accept | pending → accepted | Pickup: estimated pickup in {etaMinutes} minutes. Delivery: estimated delivery in {etaMinutes} minutes. |
| Reject | pending → cancelled (restaurant_rejected) | Restaurant declined. |
| Timeout | scheduler cancel (acceptance_timeout) | Not accepted in time. |
| Ready | kitchen preparing → ready only | Pickup: ready for pickup. Delivery: on the way. |
marketing is always false. Sender is ByteBite. Callers pass restaurantName + E.164 to explicitly. The notifications package does not look up orderId.
Events that skip
No SMS on:
- Checkout /
pending - Kitchen
preparingorcompleted - Skip or illegal kitchen 409
- Foreign restaurant 404 (Luigi
orderIdunder Mario’s path) - Failed accept / reject (409 expired or not pending)
- A second timeout tick (no-op)
Invalid phone → skip send (invalid_phone). Empty Twilio credentials → skip (not_configured). NODE_ENV=test without an injected transport → skip (network_disabled). The HTTP/order result is unchanged. SMS failures must not roll back the order write.
Channels
| Channel | Provider | This slice |
|---|---|---|
sms | Twilio Programmable Messaging | Sent on the four events above. Alphanumeric sender ByteBite. Restaurant name is in the text, not the sender. |
email | AWS SES | Client remains. Unused — no customer_email on orders. |
Marketing is always forbidden (marketing: false). WhatsApp, kitchen pushes other than Ready SMS, and WebSockets are out of scope.
Environment (optional, like Sentry)
Empty TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / SES_FROM_EMAIL (and the AWS key placeholders) → no send, no crash at API start. Clients are a documented no-op without credentials.
TWILIO_MESSAGING_SERVICE_SID is accepted as a placeholder and is unused. Sends use from: ByteBite in code.
Checkout phones stay E.164 as already stored. Checkout validation remains presence + length; the SMS path uses isE164Phone.
Isolation
A render helper takes restaurantName + E.164 (or e-mail address) explicitly. There is no orderId lookup and no cross-tenant load. The order service loads the restaurant for that restaurant_id and the phone from that tenant’s order row.
No BullMQ/Redis. Sends run after the order write commits; they are not fire-and-forget in a queue, and they must not undo the write.
API
| Method | Path | Behavior |
|---|---|---|
POST | /webhooks/twilio/status | Optional Twilio status callback. If TWILIO_AUTH_TOKEN is missing → 503. Invalid/missing X-Twilio-Signature → 401. Valid signature → 200 { received: true }. Logs MessageSid / MessageStatus only. Does not write orders, including when the form contains a foreign orderId. |
The callback is not a required webhook. Tenant lookup is skipped (same as /health). Fastify does not trust X-Forwarded-Host, but the status stub reconstructs the signed URL with X-Forwarded-Proto when Traefik terminates TLS (https://… public URL vs http://… upstream).
sendSms is called from acceptDashboardOrder, rejectDashboardOrder, expirePendingOrders, and advanceKitchenOrder (ready only). Checkout, dashboard routes, the timeout scheduler wrapper, storefront routes, and seeds do not import @bytebite/notifications. sendEmail is not imported by the order service.
Seeds
Seeds still create no orders and send nothing.
Not in this slice
Checkout sends, customer_email / e-mail column, marketing, WhatsApp, WebSocket, kitchen pushes except Ready SMS, BitePay, customer accounts, production SES verify, BullMQ/Redis, sound/PWA. Delivery copy is fulfillment-specific on accept and ready: Delivery. Dashboard session: Staff authentication. Closed checkout 409: Opening hours.