Skip to main content

NG-SOS API

The NG-SOS API is the inbound half of an integration: your system calls NG-SOS. It reports calls, creates incidents, keeps dispatcher-entered data current, reaches the caller through chat, live video or a location SMS, and closes the incident.

The outbound half, where NG-SOS calls your system, is NG-SOS Webhooks v3.

EnvironmentBase URL
Productionhttps://api.ng-sos.com
Azerothhttps://ems.azeroth.ng-sos.com

Before you start​

Obtain a client_id, a client_secret, the scopes your application may request and the agency IDs it may act for from NG-SOS. The Identity Server guide covers the token request in full; this page covers only what the API expects.

The typical flow​

  1. Get a token from NG-SOS Identity with the scopes the operations need and agency_context set to the agency you act for. Every incident operation, creation included, also needs username.required in scope and the username of the acting dispatcher. Cache it until shortly before it expires.
  2. Report the call with POST /v1/calls, and POST /v1/calls/{callId}/end when it ends, if your system handles the telephony leg.
  3. Create the incident with POST /v1/incidents. caller.phoneNumber is required. The response carries the incident ID.
  4. Keep the incident current. Each dispatcher-entered field has its own PATCH operation: call sign, caller full name, incident label, incident summary and patient description. A caller location is added with POST /v1/incidents/{incidentId}/caller/track.
  5. Reach the caller with caller/locate, which sends a location-link SMS, or with caller/chat/activate and caller/live-video/activate, which return the Portal URL to open for the dispatcher.
  6. Close the incident with POST /v1/incidents/{incidentId}/close. A closed incident rejects every further change with 409 IncidentClosed.

Every operation and its exact request and response shape is in the API reference.

PATCH or POST​

VerbMeaning
PATCH on a fieldSets a value you can set again. The five text fields also clear with an empty string.
POST on a collectionAppends an entry that stays. caller/track is the only one.
POST on a sub-resourcePerforms an action, or a transition that happens once.

A transition that already happened answers 409: IncidentClosed, CallAlreadyEnded, ChatAlreadyActive, LiveVideoAlreadyActive, CollaborationInvitationAlreadyResolved.

Routing an incident​

POST /v1/incidents creates the incident for the agency in the access token. Set routingMode to TokenAgency.

When supplied, caller.location becomes the caller's first known position - the same kind of position POST /v1/incidents/{incidentId}/caller/track adds. Both use an entered location with WGS84 coordinates and the required capturedAtUtc timestamp. The only supported source is Entered; the server records when it received the position separately. Measured positions with an accuracy and another source reach NG-SOS from devices, not over this API, and NG-SOS Webhooks v3 reports them as Location.

Adding to the caller track​

POST /v1/incidents/{incidentId}/caller/track appends a position a dispatcher established - the caller described a landmark, a colleague called back with an address. Every location you add is kept: a later one is preferred for display and supersedes the earlier one without replacing it, so the incident keeps the whole sequence of what was known and when. There is no operation to correct or remove an entry; add the better position and it takes over.

The track is the same one NG-SOS Webhooks v3 delivers back as caller.tracks, where each entry carries the source it came from. Positions you add over this API form the track for the dispatcher source; positions measured by a device form their own.

This is why it is a POST on a collection rather than a PATCH on a field. A PATCH in this API sets a value you can set again, and a caller location is not that.

The caller phone number​

POST /v1/incidents requires caller.phoneNumber. Your system takes the call, so it knows the number, and NG-SOS needs it for everything it can do for that caller afterwards: caller/locate sends the location-link SMS to it, and caller/chat/activate and caller/live-video/activate reach the caller the same way. An incident created without one could never use any of them.

The number cannot be changed over this API: there is no PATCH for it. NG-SOS may still resolve a number of its own for incidents raised elsewhere, and NG-SOS Webhooks v3 reports that as PATCH /v3/incident/{incidentId}/caller/phone-number.

Repeating a request that timed out​

POST /v1/calls recognises a repeat on a key, answers 201 when it created the resource and 200 when the key was already taken by a matching resource, and answers 409 when the key is taken by something that does not match. A 409 changes nothing.

OperationKey201200409
POST /v1/callsthe agency in the token, callerPhoneNumber and startedAtUtcthe call was createdthe call already existedCallAlreadyExists — the call already ended at a time other than the endedAtUtc you sent

Both 200 and 201 carry the same call body, so a retry needs no special handling: read the ID and carry on.

POST /v1/incidents creates a new incident with a server-generated identifier for every accepted request. Do not automatically retry it after a timeout: a retry can create another incident.

Recovering from a 409:

  • CallAlreadyExists — repeat without endedAtUtc to obtain the call ID, then end the call with POST /v1/calls/{callId}/end. There is no read operation for a call, so repeating the request is the only way to obtain its ID.

Ending a call is a transition, not a creation, and is not repeatable: a second POST /v1/calls/{callId}/end answers 409 CallAlreadyEnded.

Clearing a text field​

Every PATCH operation that carries dispatcher-entered text — call sign, caller full name, incident label, incident summary, patient description — clears the value when you send null. The property itself stays required, so send it as null rather than leaving it out. An empty string and a value of only whitespace are both rejected with 400 ValidationFailed.

curl --request PATCH "https://api.ng-sos.com/v1/incidents/$INCIDENT_ID/label" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{"label":null}'

This is the same value NG-SOS Webhooks v3 delivers for a cleared field, so a system that implements both halves can carry a value between them unchanged: the null you receive in IncidentLabelChanged is the null you send back here.

Scopes and token context​

Each operation lists the scope it requires. Beyond the scope, operations differ in what the token must identify:

ContextWhat the token request needsOperations
Agencyagency_contextGET /v1/agency/info, POST /v1/calls, POST /v1/calls/{callId}/end
Agency and userusername.required in scope, plus agency_context and usernamePOST /v1/incidents and every operation on an existing incident

An operation that needs the user context but receives an application-only token fails with 403 UserContextRequired; a missing agency context fails with 403 AgencyContextRequired.

Errors​

Every error is application/problem+json with a stable code and a type of https://ng-sos.dev/errors/{code}. Log code and traceId; branch on code, never on detail.

{
"type": "https://ng-sos.dev/errors/IncidentClosed",
"title": "Conflict",
"status": 409,
"detail": "The incident is already closed.",
"instance": "/v1/incidents/497f6eca-6276-4993-bfeb-53cbbbba6f08",
"code": "IncidentClosed",
"traceId": "00-example-trace-id"
}
StatuscodeMeaning
400ValidationFailedA field did not satisfy the documented constraints. errors names the offending fields.
400CallerPhoneNumberRequiredThe incident has no caller phone number.
400AgencyNotAllowedThe submitted agency may not be invited to this incident.
401AuthenticationRequiredA valid access token is required.
403AgencyContextRequiredThe token does not identify an agency.
403UserContextRequiredThe token does not identify a user.
403AuthorizationDeniedThe access token does not grant this operation.
404IncidentNotFoundNo incident with that ID is accessible to the token agency.
404CallNotFoundNo call with that ID is owned by the token agency.
409CallAlreadyExistsThe call already ended at a time other than the endedAtUtc you sent.
409IncidentClosedThe incident is closed and accepts no further change.
409UpdateConflictThe resource changed concurrently and could not be updated.
409ChatAlreadyActiveCaller chat is already active.
409LiveVideoAlreadyActiveLive video is already active on the incident. Activation never fails because a device cannot do video.
409CallAlreadyEndedThe call already has an end time.
409CollaborationInvitationPendingAn invitation for that agency is still open.
409CollaborationAlreadyActiveThat agency already collaborates on the incident.
409NoPendingCollaborationInvitationNo open invitation exists for the authenticated agency.
409CollaborationInvitationAlreadyResolvedThe invitation was already accepted or declined.
415UnsupportedMediaTypeSend the body as application/json.
500InternalServerErrorThe request could not be completed.

An incident that belongs to another agency answers 404 IncidentNotFound rather than 403, so the API never reveals that an incident exists.

Collaboration​

The agency that owns an incident obtains its currently eligible collaboration agencies from GET /v1/incidents/{incidentId}/collaboration/available-agencies. This incident-specific snapshot must not be cached. The invited agency accepts or declines with collaboration/accept or collaboration/decline.