Skip to main content

E911 Dispatch Addresses

When a user dials 911 from a DialStack device, the platform must tell the upstream provider where the call originates so emergency services can dispatch to the correct address. This guide explains the object model behind that lookup, how to provision and assign locations through the API, and what happens at call time.

US federal law (Kari's Law and RAY BAUM's Act) makes this dispatch obligation strict: the address must be specific enough for first responders to find the caller, and 911 must be reachable without prefixes or unlocking. DialStack handles the SIP-side mechanics; the account is responsible for keeping each device's location current as people move desks, branches, or sites.

Object model

The dispatch lookup involves two kinds of resources:

  • Location — a physical site (name, structured address). Once registered with the upstream provider it gains an e911_status and the carrier-side ID that travels with each emergency call.
  • Device — a physical thing that sits at a site:
    • Deskphone — a desktop PoE/WiFi phone. Carries its own location_id.
    • DECT base — a box that hosts one or more wireless handsets. Carries its own location_id. Every handset paired with the base inherits it, because the base radio defines where the handset can physically reach.
    • DECT handset — a cordless handset paired with a base. Has no location_id of its own.

Softphone and in-app SDK calls don't follow this model — they have no physical device. They use a per-user emergency-address flow described in Mobile & Push Notifications.

Provisioning a location for E911

Create the location with its street address, then register it for E911. The carrier validates the address and assigns a registration ID; the location is unusable for emergency dispatch until that completes.

const location = await dialstack.locations.create({
name: 'New York HQ',
address: {
address_number: '350',
street: '5th Avenue',
unit: 'Suite 4200',
city: 'New York',
state: 'NY',
postal_code: '10118',
country: 'US',
},
});

await dialstack.locations.provisionE911(location.id);

The location moves through a status machine:

e911_statusMeaning
noneNo registration attempted yet.
pendingSubmitted to the carrier; waiting for a response.
bindingCarrier accepted the address and is finalizing the dispatch record.
provisionedReady. Emergency calls from devices assigned here will dispatch to this address.
failedCarrier rejected the registration (typically a non-deliverable address). Retry from the admin portal or the API.

Only provisioned locations are usable at call time. Assigning a device to a location that is still pending or failed is allowed (so admins can pre-stage during onboarding), but the call-time lookup treats it as unassigned.

Assigning a device to a location

Set location_id on the device itself, at create time or later. The unified /v1/devices endpoint covers all three device kinds:

// Deskphone — register the MAC and bind it to a location in one call
await dialstack.devices.create({
type: 'deskphone',
mac_address: '00:04:13:aa:bb:cc',
location_id: 'loc_01h…',
});

// DECT base — shared by every handset paired with this base
await dialstack.devices.create({
type: 'dect_base',
mac_address: '00:04:13:bb:cc:dd',
location_id: 'loc_01h…',
});

// Move a base to a different site (e.g. branch office reshuffle)
await dialstack.devices.update(baseId, { location_id: 'loc_01h…' });

// Clear the dispatch location (e.g. device returned to inventory)
await dialstack.devices.update(deviceId, { location_id: null });

location_id on update is tri-state: omit to leave unchanged, send a loc_… ID to set, send explicit JSON null to clear. DECT handsets have no location_id of their own — to change where a handset dispatches, change its paired base's location_id, or re-pair the handset to a base at the target site (POST /v1/devices/:handset_id with base_id).

The Location ID must reference a location on the same account; cross-account assignment is rejected at the API layer.

Call-time resolution

When a user dials 911 (or another configured emergency number), DialStack resolves the dispatch address by walking from the calling user to the device they're assigned to:

  1. DECT handset → paired DECT base → base's location_id.
  2. Deskphone → deskphone's location_id.
  3. Softphone / SDK call → no device. The call uses the user's per-call registered emergency address instead; see Mobile & Push Notifications.

For device-backed calls, each candidate is filtered for e911_status = provisioned, status = active, and a carrier-side registration ID before being chosen. If the base's location isn't dispatchable (still pending, deleted, etc.) the lookup falls through to the deskphone candidate; if neither is dispatchable, no address is sent and the CRITICAL audit fires.

When a dispatchable location is resolved, the carrier-side registration ID is attached to the outbound INVITE so the carrier can route the call to the correct PSAP with the correct address on file.

Webhook payload

Every emergency call fires a call.emergency webhook before the call is bridged to the carrier. The payload includes:

{
"event": "call.emergency",
"data": {
"call_id": "call_01h…",
"from": "+12125550100",
"to": "911",
"endpoint_id": "ep_01h…",
"user_id": "user_01h…",
"location_id": "loc_01h…",
"e911_provisioned": true
}
}

e911_provisioned: false means the platform completed the call but could not attach a dispatch address — first responders will fall back to whatever default the carrier has on file for the underlying number, which is rarely the actual caller location. Treat this as an actionable signal: alert the admin, the front desk, or whoever is best placed to clear the misconfiguration.

This webhook is the recommended hook for any in-building notification you need to layer on top — paging a security desk, flashing a kiosk, sending an SMS to the receptionist. See Webhook Events for delivery semantics.

Failure modes

SituationWhat happens at call time
Device has no location_idCall completes, e911_provisioned: false. No dispatch address sent.
Assigned location is still pending / binding / failedSame — treated as unassigned at call time.
Location was assigned then deletedlocation_id is cleared automatically (FK on delete). Same outcome as "no location assigned" until the device is reassigned.

Each call-time failure is logged with a CRITICAL audit entry, on top of the call.emergency webhook signal, so the misconfiguration is recoverable after the fact even if the webhook handler missed the event. The audit and the webhook are independent surfaces — both fire on every emergency call regardless of dispatchability.

Testing in non-production environments

Do not place real 911 calls to test. Dial 933 instead — the carrier's non-routable test number that exercises the full dispatch flow (carrier-side address lookup, Geolocation header on the INVITE, webhook, audit) and reads the registered address back to the caller, without contacting a real PSAP. In local dev, the emergency dial-plan branch routes 911 to the same test destination so the same verification works end-to-end.