SwiftDashSwiftDash
Emergency Response

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.

ℹ️Reports arrive on the dispatch console the moment they are written — the console subscribes to the incident table directly, so there is no queue or notification step between your request returning 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.

⚠️A key compiled into a published mobile app can be extracted from the binary by anyone. Because this key can file emergencies into a live dispatch queue, prefer routing calls through your own backend, or delivering the key to the app at runtime after your own authentication, rather than shipping it inside the APK or IPA.

Base URL

POSThttps://swiftdashdms.com/api/v1/emergency/...

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

NameTypeRequiredDescription
INVALID_API_KEY401OptionalMissing key, malformed key, or a key that has been revoked.
NOT_AUTHORIZED403OptionalThe key is valid but its account is not an emergency command center.
INVALID_BODY400OptionalThe request body was not valid JSON.
VALIDATION_ERROR400OptionalA field failed validation. See the details array for which one and why.
NOT_FOUND404OptionalNo report matches that tracking token for this command center.
OUTSIDE_SERVICE_AREA422OptionalThe incident location falls outside the command center’s coverage area.
REPORT_FAILED500OptionalThe 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.

🚨On a 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

POST/api/v1/emergency/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

NameTypeRequiredDescription
incidentTypestringRequiredOne of medical, fire, or crime. Determines which agency the incident is routed to.
incidentLatnumberRequiredLatitude of the emergency itself — where help should go. Not necessarily where the reporter is.
incidentLngnumberRequiredLongitude of the emergency.
deviceIdstringRequiredA stable identifier for the reporting device (1–200 chars). Keep it: cancelling later requires the same value.
deviceLatnumberOptionalThe device’s own GPS fix. Supplying it lets a dispatcher see whether the pin matches where the reporter actually is.
deviceLngnumberOptionalLongitude of the device’s GPS fix.
descriptionstringOptionalWhat is happening, in the reporter’s words. Up to 2000 characters. Shown to the dispatcher, never on the public tracking page.
addressstringOptionalStreet address, if the app resolved one. Up to 500 characters.
landmarkstringOptionalA nearby landmark. Up to 500 characters. Often more useful to a responder than a street address.
reporterNamestringOptionalName of the person reporting. Up to 200 characters. Discarded if isAnonymous is true.
reporterPhonestringOptionalCallback number, 7–20 characters. The single most useful optional field — it lets a dispatcher ring back for detail.
isAnonymousbooleanOptionalWhen 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
  }'

Response201 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

NameTypeRequiredDescription
reportIdstringOptionalInternal incident id. Useful for your own logs; not needed by any other call here.
referenceNumberstringOptionalShort human reference, formatted MR- plus six characters. This is what a caller reads down the phone.
statusstringOptionalAlways "submitted" on creation — a dispatcher has not yet acted on it.
agencystringOptionalThe agency the incident was routed to, derived from incidentType.
trackingTokenstringOptional64 hex characters. Required by the status and cancel calls. Store it.
trackingUrlstringOptionalA public page the reporter can open or forward. No login required.
ℹ️A flagged report is not a rejected one and the response does not differ. The flag and its reason are attached to the incident for the dispatcher, who decides. See Review Flags.
⚠️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

GET/api/v1/emergency/status/{trackingToken}

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"

Response200 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

NameTypeRequiredDescription
statusstringOptionalCurrent incident status. See Incident Statuses.
isActivebooleanOptionalFalse once the incident is resolved, cancelled or rejected. Stop polling when this turns false.
unitsarrayOptionalUnits 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.
etaMinutesnumberOptionalSoonest arrival across all responding units, or null. This is the number to show a waiting person.
closureReasonstringOptionalWhy the incident was closed, when it has been. Null while active.
timestampsobjectOptionalISO timestamps for dispatched, accepted, onScene and resolved. Null until each happens.
updatedAtstringOptionalWhen the incident last changed. Useful for deciding whether to re-render.
ℹ️An unknown token and a token belonging to a different command center both return the same 404 NOT_FOUND. Distinguishing them would let a caller probe for valid tokens.

Cancel a Report

POST/api/v1/emergency/cancel

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

NameTypeRequiredDescription
trackingTokenstringRequiredThe token returned when the report was filed.
deviceIdstringRequiredThe same deviceId the report was filed with. Must match exactly.
reasonstringOptionalWhy it is being cancelled, in the reporter’s words. Recorded on the incident and shown to the dispatcher. Defaults to "Cancelled by the reporter".
⚠️Only the reporting device may cancel. A tracking link is meant to be forwarded — to family, into group chats — so holding the link cannot be enough to call off someone else’s ambulance. The matching 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"
  }'

Response200 OK

{
  "referenceNumber": "MR-A3F91C",
  "status": "cancelled",
  "unitsStoodDown": 1
}

Failure codes specific to this call

NameTypeRequiredDescription
NOT_REPORTER403OptionalThe deviceId does not match the one the report was filed with. Someone holding a forwarded link cannot cancel.
ALREADY_CLOSED409OptionalThe incident is already resolved, cancelled or rejected. The body includes the current status.
UNIT_ON_SCENE409OptionalA 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_FAILED500OptionalThe cancellation could not be written. Tell the user to call and say help is no longer needed.
ℹ️Units that were still travelling are recorded as stood down, not resolved. They did not do the work, and keeping that distinction is what stops cancelled calls from flattering the city’s response statistics.

Tracking Endpoint

GET/api/track/emergency/{trackingToken}

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/9f3c1ab24e7d40f8b6a15c8e2d094fb7c3e58a1d6b2f47c09e83a15d7b6c024f

Response200 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

NameTypeRequiredDescription
locationobjectOptionalWhere the emergency is. Included here because this response has to draw a map.
positionsarrayOptionalLast known position of each responding unit. A fix older than five minutes is omitted rather than drawn somewhere the unit has long since left.
incidentTypestringOptionalmedical, fire or crime — used to colour the page.
commandCenterobjectOptionalName and callback number of the command center handling the incident.
brandingobjectOptionalThe command center’s presentation settings. Colours are preferences, not instructions — check them for contrast before applying them to your own surface.
channelstringOptionalRealtime channel carrying live unit positions, for clients that want movement between polls rather than at poll rate.
ℹ️This endpoint deliberately omits the reporter’s name, phone and description for the same reason the page does. If your app needs those, it already has them — it sent them.

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.

StatusActiveMeaning
submittedYesFiled and waiting on the dispatch board. No unit assigned yet.
dispatchedYesA dispatcher has assigned at least one unit, which has not yet set off.
en_routeYesA unit has accepted and is travelling to the scene.
on_sceneYesA unit has arrived. From this point the report can no longer be cancelled from the app.
resolvedNoResponders have finished. Reassigning a unit reopens the incident — fires reignite.
cancelledNoCalled off, either by the reporter or by a dispatcher.
rejectedNoA 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.

NameTypeRequiredDescription
medicalCDRRMO_AMBULANCEOptionalInjury, collapse, difficulty breathing, anything needing an ambulance.
fireBFPOptionalFire, smoke, or a fire risk. Routed to the Bureau of Fire Protection.
crimePNPOptionalAssault, 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.

NameTypeRequiredDescription
No GPS fixflagOptionaldeviceLat 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 GPSflagOptionalThe 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 deviceflagOptionalThree or more reports from the same deviceId within 24 hours. The count is recorded on the incident.
Repeat phoneflagOptionalThree 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.
⚠️The phone rule flags; it never blocks. Because the number is not verified, anyone can type anyone else’s — so a limit that refused reports would let one abuser silence a stranger’s real emergency. It becomes a far stronger signal once the number is verified with an OTP at the app’s end, which is worth doing: a verified phone survives reinstalls and handset changes, which deviceIddoes not.
ℹ️None of these are rate limits. There is no request throttle on emergency reporting, deliberately — the same person reporting three times in an hour may be describing a situation that keeps getting worse.

Looking for parcels, couriers and fleet endpoints? Those live in the SwiftDash Delivery API reference.