Emergency Response API
File emergencies from a citizen app into a command center’s dispatch console, follow the response as it happens, and call it off if help is no longer needed.
This is a separate API from SwiftDash Deliveries. It shares authentication and error conventions with it and nothing else — different endpoints, different objects, different statuses.
Introduction
The API is built around one guiding rule: a real emergency must never be rejected by a machine. A person reporting a fire is frightened, possibly typing one-handed, possibly wrong about the details. Refusing them because a field looked suspicious would be the worst failure this system could have.
So there are exactly two hard rejections: a malformed request, and a location outside the area the command center covers. Everything else that looks unusual — a missing GPS fix, a pin far from the reporter, a device that has reported repeatedly — is flagged for a human dispatcher to judge, and the report still lands on the board. Even the geofence check fails open: if it errors, the report is accepted.
1. File a report
POST the emergency type and location. You get a reference number and a tracking token back.
2. Follow it
Poll the status endpoint while the incident is active, or open the tracking link.
3. Cancel if needed
False alarm, or help arrived another way. Only the reporting device may do this.
201 and a dispatcher seeing the call.Authentication
Every request carries an API key in the x-api-key header. Keys begin with sd_live_ and are issued from the command center’s dashboard under Settings → API & Webhooks.
curl https://swiftdashdms.com/api/v1/emergency/status/TOKEN \
-H "x-api-key: sd_live_your_key_here"The key identifies which command center receives the report. It does not identify the citizen — there is no user login in this API, by design. A key belonging to a delivery business is rejected with NOT_AUTHORIZED, so a courier account can never inject incidents into a city’s dispatch queue.
Base URL
All emergency endpoints run in the sin1 (Singapore) region rather than SwiftDash’s default US East, because they serve Philippine cities and the round trip is the dominant cost. A report typically completes in 0.3–0.6s end to end. Every response carries an x-response-time header measuring the server’s own share of that.
Requests and responses are JSON. Status responses are sent Cache-Control: no-store — a cached “submitted” while a responder is already on scene would be worse than an extra round trip.
Errors
Errors return a non-2xx status and a JSON body with a human-readable error and a stable code. Branch on the code; the message is written to be shown to a person and may be reworded.
{
"error": "This location is outside the area this command center covers. Please call your local emergency hotline directly.",
"code": "OUTSIDE_SERVICE_AREA"
}Codes used across all emergency endpoints
| Name | Type | Required | Description |
|---|---|---|---|
INVALID_API_KEY | 401 | Optional | Missing key, malformed key, or a key that has been revoked. |
NOT_AUTHORIZED | 403 | Optional | The key is valid but its account is not an emergency command center. |
INVALID_BODY | 400 | Optional | The request body was not valid JSON. |
VALIDATION_ERROR | 400 | Optional | A field failed validation. See the details array for which one and why. |
NOT_FOUND | 404 | Optional | No report matches that tracking token for this command center. |
OUTSIDE_SERVICE_AREA | 422 | Optional | The incident location falls outside the command center’s coverage area. |
REPORT_FAILED | 500 | Optional | The report could not be written. Tell the user to call their hotline directly. |
Validation failures list every offending field at once rather than the first, so an app can correct a form in one pass:
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": [
{
"field": "incidentType",
"message": "Must be one of: medical, fire, crime",
"code": "INVALID_VALUE"
},
{
"field": "incidentLat",
"message": "Must be a number between -90 and 90",
"code": "INVALID_RANGE"
}
]
}Each entry carries a code from a fixed set — REQUIRED, INVALID_TYPE, INVALID_RANGE, INVALID_FORMAT, INVALID_VALUE, TOO_LONG, TOO_SHORT — so a form can be mapped to field errors without parsing the message text.
500, or on a network failure, do not silently retry forever. Show the caller the local emergency hotline. An app that spins while someone waits for an ambulance is worse than one that admits it failed.File a Report
Files an emergency into the command center’s dispatch queue. Returns the reference number the caller can quote on the phone, and the tracking token every later call is keyed on.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
incidentType | string | Required | One of medical, fire, or crime. Determines which agency the incident is routed to. |
incidentLat | number | Required | Latitude of the emergency itself — where help should go. Not necessarily where the reporter is. |
incidentLng | number | Required | Longitude of the emergency. |
deviceId | string | Required | A stable identifier for the reporting device (1–200 chars). Keep it: cancelling later requires the same value. |
deviceLat | number | Optional | The device’s own GPS fix. Supplying it lets a dispatcher see whether the pin matches where the reporter actually is. |
deviceLng | number | Optional | Longitude of the device’s GPS fix. |
description | string | Optional | What is happening, in the reporter’s words. Up to 2000 characters. Shown to the dispatcher, never on the public tracking page. |
address | string | Optional | Street address, if the app resolved one. Up to 500 characters. |
landmark | string | Optional | A nearby landmark. Up to 500 characters. Often more useful to a responder than a street address. |
reporterName | string | Optional | Name of the person reporting. Up to 200 characters. Discarded if isAnonymous is true. |
reporterPhone | string | Optional | Callback number, 7–20 characters. The single most useful optional field — it lets a dispatcher ring back for detail. |
isAnonymous | boolean | Optional | When true, name and phone are never stored — not merely hidden. The report still reaches the console. |
curl -X POST https://swiftdashdms.com/api/v1/emergency/report \
-H "x-api-key: sd_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"incidentType": "medical",
"incidentLat": 11.5853,
"incidentLng": 122.7511,
"deviceLat": 11.5851,
"deviceLng": 122.7509,
"deviceId": "a7f3c9e1-device-install-id",
"description": "Elderly man collapsed near the plaza, breathing but unresponsive",
"address": "Roxas City Plaza, Capiz",
"landmark": "In front of the cathedral",
"reporterName": "Maria Santos",
"reporterPhone": "+639171234567",
"isAnonymous": false
}'Response — 201 Created
{
"reportId": "3f2a1b8c-9d4e-4a7f-b1c2-8e5d6a9f0b3c",
"referenceNumber": "MR-A3F91C",
"status": "submitted",
"agency": "CDRRMO_AMBULANCE",
"trackingToken": "9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f",
"trackingUrl": "https://swiftdashdms.com/track/emergency/9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f"
}Response fields
| Name | Type | Required | Description |
|---|---|---|---|
reportId | string | Optional | Internal incident id. Useful for your own logs; not needed by any other call here. |
referenceNumber | string | Optional | Short human reference, formatted MR- plus six characters. This is what a caller reads down the phone. |
status | string | Optional | Always "submitted" on creation — a dispatcher has not yet acted on it. |
agency | string | Optional | The agency the incident was routed to, derived from incidentType. |
trackingToken | string | Optional | 64 hex characters. Required by the status and cancel calls. Store it. |
trackingUrl | string | Optional | A public page the reporter can open or forward. No login required. |
isAnonymous: true discards the reporter’s name and phone at the point of writing — they are never stored, so they cannot be recovered afterwards, and a dispatcher cannot call back. Only send it when the reporter has actually chosen anonymity.Get Status
Returns the current state of one incident. Designed to be polled every 15–30 seconds while the incident is active — polling rather than push, so your app needs no backend, no webhook endpoint and no signature verification. A dropped response simply retries on the next tick.
curl https://swiftdashdms.com/api/v1/emergency/status/9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f \
-H "x-api-key: sd_live_your_key_here"Response — 200 OK
{
"referenceNumber": "MR-A3F91C",
"status": "en_route",
"isActive": true,
"agency": "CDRRMO_AMBULANCE",
"units": [
{
"callsign": "AMB-01",
"agency": "CDRRMO_AMBULANCE",
"status": "en_route",
"etaMinutes": 6
}
],
"etaMinutes": 6,
"closureReason": null,
"timestamps": {
"dispatched": "2026-08-05T09:12:04.881Z",
"accepted": "2026-08-05T09:12:41.002Z",
"onScene": null,
"resolved": null
},
"trackingUrl": "https://swiftdashdms.com/track/emergency/9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f",
"updatedAt": "2026-08-05T09:13:10.447Z"
}Response fields
| Name | Type | Required | Description |
|---|---|---|---|
status | string | Optional | Current incident status. See Incident Statuses. |
isActive | boolean | Optional | False once the incident is resolved, cancelled or rejected. Stop polling when this turns false. |
units | array | Optional | Units currently dispatched, en route, or on scene. Units that declined or were stood down are omitted — that is internal churn the reporter should not see. |
etaMinutes | number | Optional | Soonest arrival across all responding units, or null. This is the number to show a waiting person. |
closureReason | string | Optional | Why the incident was closed, when it has been. Null while active. |
timestamps | object | Optional | ISO timestamps for dispatched, accepted, onScene and resolved. Null until each happens. |
updatedAt | string | Optional | When the incident last changed. Useful for deciding whether to re-render. |
404 NOT_FOUND. Distinguishing them would let a caller probe for valid tokens.Cancel a Report
Calls off a report the reporter no longer needs — a false alarm, a fire they put out themselves, a patient a neighbour already drove to hospital. Units still travelling are stood down, and the incident closes as cancelled.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
trackingToken | string | Required | The token returned when the report was filed. |
deviceId | string | Required | The same deviceId the report was filed with. Must match exactly. |
reason | string | Optional | Why it is being cancelled, in the reporter’s words. Recorded on the incident and shown to the dispatcher. Defaults to "Cancelled by the reporter". |
deviceId is what proves the caller is the original reporter.curl -X POST https://swiftdashdms.com/api/v1/emergency/cancel \
-H "x-api-key: sd_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"trackingToken": "9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f",
"deviceId": "a7f3c9e1-device-install-id",
"reason": "False alarm — he is awake and talking now"
}'Response — 200 OK
{
"referenceNumber": "MR-A3F91C",
"status": "cancelled",
"unitsStoodDown": 1
}Failure codes specific to this call
| Name | Type | Required | Description |
|---|---|---|---|
NOT_REPORTER | 403 | Optional | The deviceId does not match the one the report was filed with. Someone holding a forwarded link cannot cancel. |
ALREADY_CLOSED | 409 | Optional | The incident is already resolved, cancelled or rejected. The body includes the current status. |
UNIT_ON_SCENE | 409 | Optional | A responder has already arrived. Cancelling through an app is the wrong tool at that point — the dispatcher closes it out after speaking to them. |
CANCEL_FAILED | 500 | Optional | The cancellation could not be written. Tell the user to call and say help is no longer needed. |
Tracking Link
Every report comes back with a trackingUrl. It opens a public page that needs no account and no app — the reporter can open it, or send it to a relative who is not near the phone that reported.
The page shows the stage the response has reached, the estimated arrival, the responding unit’s callsign, a live map of the unit as it travels, and a button to call the command center. It updates itself; there is nothing to refresh.
It carries the command center’s own branding — logo, colours, tagline and per-stage wording, all configured under Settings → Branding in the dashboard. Nothing about the page needs to be built or hosted by you.
Tracking Endpoint
The data behind that page, should you want to build your own view of it in the app instead of opening a browser. Note the path: this one sits outside /v1 and takes no API key. The tracking token is the credential, which is what lets it work in a browser nobody has logged into.
curl https://swiftdashdms.com/api/track/emergency/9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024fResponse — 200 OK, abridged
{
"referenceNumber": "MR-A3F91C",
"status": "en_route",
"isActive": true,
"incidentType": "medical",
"address": "Roxas City Plaza, Capiz",
"landmark": "In front of the cathedral",
"location": { "lat": 11.5853, "lng": 122.7511 },
"units": [
{ "callsign": "AMB-01", "agency": "CDRRMO_AMBULANCE", "status": "en_route", "etaMinutes": 6 }
],
"etaMinutes": 6,
"positions": [
{ "callsign": "AMB-01", "lat": 11.5893, "lng": 122.7551 }
],
"commandCenter": { "name": "RCERT", "phone": "+639673871221" },
"channel": "tracking:3f2a1b8c-9d4e-4a7f-b1c2-8e5d6a9f0b3c",
"timestamps": { "reported": "2026-08-05T09:11:20.114Z", "dispatched": "..." },
"branding": { "logoUrl": "...", "statusLabels": {} },
"updatedAt": "2026-08-05T09:13:10.447Z"
}Fields that differ from the key-authenticated status call
| Name | Type | Required | Description |
|---|---|---|---|
location | object | Optional | Where the emergency is. Included here because this response has to draw a map. |
positions | array | Optional | Last known position of each responding unit. A fix older than five minutes is omitted rather than drawn somewhere the unit has long since left. |
incidentType | string | Optional | medical, fire or crime — used to colour the page. |
commandCenter | object | Optional | Name and callback number of the command center handling the incident. |
branding | object | Optional | The command center’s presentation settings. Colours are preferences, not instructions — check them for contrast before applying them to your own surface. |
channel | string | Optional | Realtime channel carrying live unit positions, for clients that want movement between polls rather than at poll rate. |
Incident Statuses
An incident’s status is derived from its units, not set directly. Assigning the first unit moves it to dispatched; that unit accepting moves it to en_route, and so on.
| Status | Active | Meaning |
|---|---|---|
| submitted | Yes | Filed and waiting on the dispatch board. No unit assigned yet. |
| dispatched | Yes | A dispatcher has assigned at least one unit, which has not yet set off. |
| en_route | Yes | A unit has accepted and is travelling to the scene. |
| on_scene | Yes | A unit has arrived. From this point the report can no longer be cancelled from the app. |
| resolved | No | Responders have finished. Reassigning a unit reopens the incident — fires reignite. |
| cancelled | No | Called off, either by the reporter or by a dispatcher. |
| rejected | No | A dispatcher reviewed it and sent no unit. |
Treat this list as open. Branch on isActive rather than enumerating terminal statuses, so a status added later does not leave your app polling a finished incident forever.
Types & Agencies
incidentType determines routing. The mapping is fixed; you do not choose the agency.
| Name | Type | Required | Description |
|---|---|---|---|
medical | CDRRMO_AMBULANCE | Optional | Injury, collapse, difficulty breathing, anything needing an ambulance. |
fire | BFP | Optional | Fire, smoke, or a fire risk. Routed to the Bureau of Fire Protection. |
crime | PNP | Optional | Assault, theft in progress, threat to a person. Routed to the Philippine National Police. |
Review Flags
A report is flagged when something about it deserves a dispatcher’s attention. A flag never blocks the report and is not visible in the API response — it is a note attached to the incident, with its reason, for the person deciding whether to commit a unit.
| Name | Type | Required | Description |
|---|---|---|---|
No GPS fix | flag | Optional | deviceLat and deviceLng were not supplied, so the pinned location could not be corroborated. Sending them is the easiest way to avoid this. |
Pin far from GPS | flag | Optional | The reported location is more than 200 metres from the device’s own fix. Legitimate when reporting something seen at a distance — which is exactly why a human judges it. |
Repeat device | flag | Optional | Three or more reports from the same deviceId within 24 hours. The count is recorded on the incident. |
Repeat phone | flag | Optional | Three or more reports carrying the same reporterPhone within 24 hours, counted across devices. Numbers are normalised first, so +639171234567, 09171234567 and 639171234567 are one number. Not applied to anonymous reports, whose phone is never stored. |
deviceIddoes not.Looking for parcels, couriers and fleet endpoints? Those live in the SwiftDash Delivery API reference.