Skip to main content

Emergency mass notification for campuses: a decision-to-live walkthrough

This walkthrough takes a school district, college, or multi-building campus from the first posture decision to a live, rehearsed emergency notification setup. It composes pieces that already ship: voice broadcasts with an SMS fallback waterfall, push categories, Kari’s Law on-site 911 fan-out, quiet-hours configuration, and the quiet-hours reachability preview. The same shape works for K-12 districts, higher-ed campuses, and property managers; the examples below use one fictional K-12 district, Riverside Unified, 14 buildings, 9,800 students and guardians, 1,100 staff. Every control on this page is tenant-owned. Your organization decides the posture: which events alert, which audiences each event reaches, which windows apply, and how drills run. Orbit provides the channels and the controls; it does not attest to any state or federal alerting mandate on your behalf. If your program answers to a mandate, your team maps these controls to it.

1. The decisions you own

Before any configuration, write down five decisions. The rest of this page is the execution of them.
  1. Audience model. Which contact fields and segments describe your community (roles, buildings, guardian relationships)?
  2. Alert copy. What does a real alert say, in how many characters, in which languages, with which link?
  3. Cascade. Which channels carry an alert, in which order, with which per-step window and cost ceiling, and which quiet-hours posture applies to a real event versus a drill?
  4. Drill cadence. How often you rehearse, from which account, so a drill can never alter your production audience or suppression lists.
  5. Two-way and read-after. How a recipient reports “I’m safe”, where that reply lands, and which delivery record closes the loop.

2. Model the audience

Contact fields and segments per role and building

Start from the fields the cascade and the personalization need, not from a generic contact record:
  • phone in E.164 for the voice and SMS hops, one row per reachable person.
  • A role field (guardian, student, staff, security, faculty) and a building field (north-campus, dorms, athletics). Create them as custom fields if your import does not carry them.
  • A guardian linkage so a student row and a guardian row resolve to the same household when you compose per-household copy.
  • preferred_language when you send translated variants (per-language recipes).
Build one segment per decision unit you will actually alert: guardians-north-campus, staff-all-buildings, security-desk, dorm-residents. Use CDP segments for attribute-driven groups and static contact lists for rosters you maintain by hand, such as the security desk. Keep an all-campus segment as the union for full-scale events; build it from the per-building segments rather than as a second copy of the data.

Reachability preview: count what each channel can reach now

Before the first send, produce three counts per channel: reachable now, held by quiet hours, and suppressed.
  • Voice and SMS. The quiet-hours preview endpoint resolves the effective window per recipient and channel. Sample each segment with GET /api/v1/compliance/quiet-hours/preview?phone=<E.164>&channel=<channel> and tally the outcomes; quiet-hours configuration walks the preview and the resolved-window record. Remember the asymmetry: a recipient whose local time cannot be resolved is refused on the voice hop by the federal dialing guard, while the SMS gate is your configuration to run.
  • Push. A recipient receives push only if a device token is registered for them. Count registered devices per segment from your push device token surface (push device tokens); everyone without a token is absent from the push leg by construction, which is exactly why push never stands alone in this cascade.
  • Suppression. Run the tenant do-not-call and opt-out sources against each segment before launch (DNC pre-flight scrub). The scrubbed count is your third number.
Write the counts next to the posture document. They are the baseline every drill report compares against, and they surface the two traps early: guardians with no app install (push-blind) and guardians with no resolvable timezone (voice-blind).

3. Compose the alert

Write the alert once, bounded per channel:
  • Voice script. One spoken paragraph, 20 to 30 seconds when read aloud. Name the event, the action, and the source of updates. No personalization tokens in the voice leg except a first name at the top; a long interpolated script reads badly.
  • SMS. Keep it to one 160-character segment where your posture allows it; every extra segment is extra cost and extra latency at scale. Put the one action and one short link to your status page in the body, and decide your opt-out phrasing per message class (opt-out rules).
  • Push. Title carries the event class, body carries the action. The interactive category adds the buttons (section 4), so the body stays short.
Personalize with the template variables your contact model carries, for example {{first_name}} and {{building}}, so a north-campus lockdown never tells a south-campus guardian to shelter. Preview the rendered copy per segment before the first live use (campaign personalization preview shows the picker and preview flow). Example set for a weather closure:
For multilingual communities, store one variant per preferred_language segment and send the variants as parallel legs of the same launch, not as afterthoughts.

4. Build the cascade

The cascade is three legs with different semantics, fired as one launch:
  1. Push, immediate at T+0, to every registered device in the audience.
  2. Voice, immediate at T+0 per recipient, the primary reach for everyone push cannot cover.
  3. SMS, armed as the fallback hop, firing per recipient only when the voice hop terminally fails inside the fallback window.
Push is not a hop in the voice-to-SMS waterfall, and understanding why keeps the payload honest: the notify waterfall escalates per recipient across E.164 hops on terminal delivery receipts, while push addresses device tokens and has no per-recipient DLR to escalate from. Fire the push fan-out at T+0 with POST /api/v1/push/send; it costs no per-segment carrier spend and lands on app devices while the voice calls are still ringing. The waterfall then guarantees the per-recipient escalation on the phone-number side.

Push leg: a curated emergency category

Define the category once, with an interactive surface, then reference it on every emergency send (push notification categories):
Every emergency push then names "notification_channel": "CAMPUS_EMERGENCY", and the two actions feed the same two-way accounting as the SMS keyword in section 6.

Voice to SMS leg: the waterfall payload

Reuse the two-binding waterfall shape from the voice broadcast with SMS fallback guide, with campus copy and an emergency-sized window:
Per-step window and pricing as your posture dials them:

Quiet hours: real events versus drills

The two event classes get different quiet-hours postures, and both are your configuration:
  • Real emergency alerts. The voice hop always stays inside the federal 8 AM to 9 PM recipient-local window; no workspace setting relaxes it, for any message class. For the SMS and push legs, the windows are tenant-owned: set the per-channel quiet-hours window to the posture your program chose for alerts, from always-open to business-hours-only, via quiet-hours configuration and its per-channel overrides. Decide deliberately, write the decision down, and confirm it with the preview endpoint before the first alert.
  • Daily and scheduled drills. Drills run inside your normal quiet hours, on purpose. A drill that respects the windows proves the gates your real traffic will face; a drill at 2 PM exercises exactly the path a 2 PM event takes. The narrow case is rehearsal, not convenience. Where a per-campaign window applies to a drill lane, remember a campaign override can only narrow the window it inherits (per-campaign quiet hours).
The refusal shapes you will see if you get this wrong are in the waterfall troubleshooting list: TCPA_FEDERAL_DIALING_WINDOW_BLOCKED on the voice hop is a schedule fact, not a configuration error, and the SMS hop still catches those recipients when your SMS posture allows it.

5. Schedule drills without touching production lists

Run every recurring drill from a staging subaccount (or the multi-environment split if you already operate one). The reason is the opt-out path: a STOP reply writes to the suppression list of the account that sent. A guardian who replies STOP to a drill must not disappear from a real alert; separate accounts make that structurally impossible, where a shared account would require you to maintain exclusion lists by hand. If a single account is your reality, use suppression exclusions instead: keep a drill-audience segment of consenting volunteers and exclude the drill list from every production send, and exclude production suppression writes from the drill audience. Either way, the rule is the same: drill traffic and production traffic never share a suppression list. On cadence and mechanics:
  • Schedule the recurring drill with message scheduling so the rehearsal fires on time without a human remembering it.
  • Prefix drill copy with DRILL in every channel, including the voice script and the push title.
  • Rehearse the full cascade, not one channel: push fan-out, voice dials, the SMS fallback on unanswered calls, and the two-way replies in section 6.
  • After each drill, compare the per-channel counts against the section 2 baseline and file the difference as either a data fix (missing token, missing timezone) or a posture decision.

6. Two-way: account for “I’m safe”

A one-way alert leaves the security desk counting silence. Wire the reply path before you need it:
  • SMS keyword. Add a SAFE keyword auto-reply rule (keyword auto-reply rules, with worked shapes in keyword rules recipes). A SAFE reply lands in the Inbox thread of the sending number, gets the rule’s acknowledgement automatically, and is tagged so the desk can watch the roll call build in Inbox instead of refreshing a spreadsheet. A HELP rule routes to a staffed queue.
  • Push actions. The IM_SAFE and NEED_HELP actions from section 4 return through the same Inbox surface for app users.
  • Voice. Ask for a keypad confirmation in the script (“press 1 to acknowledge”); answered calls terminate the waterfall at hop 0 and their outcome shows in the per-hop receipt, so acknowledgements by voice count in the same ledger.
Read the tally in insights: replies by keyword and conversation volume per sender, next to the delivery record below, give you reached-versus-confirmed as two numbers the incident lead can act on.

7. Kari’s Law companion: point the 911 fan-out at the security desk

Mass notification and on-site 911 notification answer different questions: the cascade tells the community what is happening; the Kari’s Law fan-out tells your safety desk that someone just dialed 911, so staff can meet responders and check on the caller. Configure it to land where your desk already watches:
Then run the verify end-to-end section of that guide as part of every term’s rehearsal: from a test extension, dial 911 on each surface you ship, expect the EMERGENCY_CALLING_NOT_SUPPORTED reject (that reject is what triggers the fan-out), and confirm the email, the signed webhook POST, and the Slack message all arrive. Pair the fan-out with current per-number emergency addresses so the alert that names a caller also resolves to a building and room, and rehearse the call path separately with the 933 drill mode. Whether Kari’s Law or a state analogue binds your organization is your counsel’s question; the notification and the location records are controls you own either way.

8. Read after: receipts, follow-up segment, report

Close the loop in three reads. Per-hop receipts. For every waterfall, GET /api/v1/notify/:notifyId returns the hop list: per-hop status, per-hop price, and per-hop error codes, plus delivered_channel naming which leg landed the recipient (track a cascade reads every column). Push results arrive per device in the send response’s notifications[], with provider errors such as BadDeviceToken on stale tokens. Missed-recipient follow-up segment. The recipients who matter after the send are the ones every hop missed: voice failed, SMS undelivered inside the window, no device token. Export those rows from the delivery log (column choices in exports) and load them as a missed-<event> segment. That segment gets the next, different leg: a staff phone tree, a front-desk call list, or a physical sweep. Never re-run the same cascade at the same audience hoping for a different result. Post-drill report. Export the drill run as your standing report: per-channel reached counts against the section 2 baseline, acknowledgements from section 6, hop prices from the receipts, and the Kari’s Law dispatch entry from your audit log, which records whether each configured channel delivered. Scheduled reports can deliver it to the owners after every drill so the report exists before anyone asks for it.

See also