SCAD Maps.Log inRequest a demo

REST API

Authentication, conventions, rate limits and the main endpoints for notes, areas, tasks, uploads and chat.

Everything the SCAD Maps app does goes through its REST API, and the same API is available to your own scripts and tools, such as a dashboard on a wall screen or a script that files incidents from another system. This page describes how to authenticate, the conventions that apply everywhere, the limits and the main endpoints. Live updates arrive over a separate connection, described in Realtime events.

Base address and format#

  • Every endpoint lives under /api on the instance's own address, for example https://your-instance/api/notes. Each instance has its own API, its own accounts and its own data.
  • Requests and responses are JSON (Content-Type: application/json), except uploads, which send the raw file.
  • Times are ISO 8601 in UTC. Coordinates are decimal degrees, WGS 84, sent as latitude and longitude. Bounding boxes use south, west, north and east.
  • Identifiers are opaque strings. Do not parse them.

Authentication#

You sign in with an account and receive a short-lived access token. Send it on every request:

Authorization: Bearer <accessToken>
EndpointPurpose
POST /api/auth/loginBody { "identifier": "...", "password": "...", "remember": true }, where the identifier is the username or the email address. Returns the access token and the user.
POST /api/auth/refreshExchanges the refresh token for a new access token and a new refresh token. Scripts send { "refreshToken": "..." } in the body; a browser sends its cookie plus an X-CSRF-Token header.
POST /api/auth/logoutEnds the session.
GET /api/auth/meThe signed-in user with access level, roles and flags.
PUT /api/auth/me/settingsSaves the caller's own settings.
POST /api/auth/me/accountChanges the caller's own email or password; needs the current password.
GET /api/auth/configPublic. Whether self-signup is on, and the instance's default workspace layout.
  • The access token is valid for 15 minutes. Refresh a little before it expires, and always store the new refresh token: each one can be used once, and using an old one ends the whole session.
  • Use a dedicated account for an integration, with the lowest access level that does the job. There are no separate API keys.
  • Instances with single sign-on still accept local accounts for integrations; see Single sign-on.
  • Accounts that are pending or rejected cannot sign in, and every request is checked against the account's current status and access level.

Conventions#

TopicBehaviour
ErrorsA non-2xx status with a JSON body { "error": "message" }. The message is meant for a person, not for parsing.
Status codes400 invalid input, 401 missing or expired token, 403 not allowed, 404 not found or not visible to you, 409 conflict with the current state, 429 rate limit.
VisibilityLists only contain what the caller may see. A record that exists but is hidden from the caller answers 404, not 403.
PaginationLists that can be large take page and pageSize (at most 500) and answer { "items": [...], "page": 1, "pageSize": 100 }. Loop until a page comes back shorter than pageSize.
UpdatesPUT on a note replaces its editable fields; PATCH on a task changes only the fields you send. A field you leave out is kept, including attachments and checklists.
TextText is sanitised on the server. HTML is not interpreted.
UploadsFiles are uploaded first and linked to a record afterwards, by passing their identifiers when creating or updating it.

Rate limits#

ScopeDefault
All /api requests, per network address600 per minute
Creating, editing and deleting notes, areas and tasks, per user60 per minute. Admins are exempt.
Sending chat messages, per user40 per minute. Admins are exempt.
Sign-in, per account name and address8 attempts per 15 minutes

A rate-limited request answers 429, and sign-in also sends a Retry-After header. Wait that long, or back off exponentially. Static tiles and images do not count towards the general limit. Operators can change the values; see Instance configuration.

Notes#

RequestWhat it does
GET /api/notes?south=&west=&north=&east=Notes inside the box, paginated. archived=1 lists archived notes instead.
GET /api/notes/:idOne note.
GET /api/notes/:id/historyIts change history.
POST /api/notesCreate. Body: content (1 to 500 characters), latitude and longitude together or neither (neither makes a personal note), optional guestVisible, checklist, attachmentIds.
PUT /api/notes/:idEdit. Author or Admin.
PATCH /api/notes/:id/checklistTick one item with { "itemId": "...", "done": true }, or, for the author, replace the list with { "checklist": [...] }.
POST /api/notes/:id/archive, /unarchiveSoft archive and restore. Author or Admin.
DELETE /api/notes/:idDelete. Author or Admin.

Areas#

RequestWhat it does
GET /api/zonesAll areas the caller may see.
POST /api/zonesCreate. Body: name (up to 120 characters), points (3 to 500 corners, outline must not cross itself), severity (CRITICAL, HIGH, MEDIUM or INFO), color, note, checklist, attachmentIds. Turning on geofence or capacity needs User Admin or Admin.
PATCH /api/zones/:id, DELETE /api/zones/:idEdit or delete. Creator or Admin.
PATCH /api/zones/:id/checklistThe same tick-or-replace rules as for notes.

Tasks and incidents#

Tasks and incidents share one set of endpoints. The kind field is TASK or INCIDENT.

RequestWhat it does
GET /api/tasksList with filters: a bounding box, kind, status and priority (comma separated), tags (any of), assignedTo, roleId, includeClosed=1, includeFuture=1, archived=1 and page.
GET /api/tasks/myTasks assigned to the caller.
GET /api/tasks/unplacedTasks with no location yet.
GET /api/tasks/optionsThe known choices: statuses, priorities, assignees, categories and tags.
GET /api/tasks/:id, /historyOne task, and its change history.
POST /api/tasksCreate. Only title (1 to 140 characters) is required. Also accepts description (1,000), latitude with longitude, priority, status, assignees, dueAt, visibleFrom, occurredAt, slaMinutes, checklist, tags, markerLabel, markerColor and attachmentIds.
PATCH /api/tasks/:idChange status, assignment or any field you send.
POST /api/tasks/:id/duplicateCopy a task.
POST /api/tasks/:id/ack, /unackAcknowledge an incident, or withdraw the acknowledgement.
POST /api/tasks/:id/archive, /unarchiveArchive and restore.
DELETE /api/tasks/:idDelete. Creator or Admin.

Statuses are OPEN, IN_PROGRESS, BLOCKED, RESOLVED and CLOSED; priorities are CRITICAL, HIGH, MEDIUM and LOW. Closed tasks are left out of lists unless you ask for them.

Uploads#

POST /api/uploads takes the raw file as the request body. The Content-Type header decides how it is handled:

KindTypesLimit
ImageWebP, JPEG, PNG2 MB, re-encoded to WebP
DocumentPDF, plain text, CSV10 MB
Voice noteMP4/AAC, WebM, Ogg, MPEG; send X-Audio-Duration in milliseconds1 MB, removed after 7 days

The answer describes a pending attachment with an identifier. Pass that identifier in attachmentIds when you create or update the note, area, task or chat message it belongs to. Pending files that are never linked are deleted after a few hours. GET /api/uploads/usage shows the caller's used space against the 50 MB quota. Guests cannot upload.

Chat#

RequestWhat it does
GET /api/chat/channelsChannels the caller belongs to, the roster and the caller's own id.
GET /api/chat/channels/:token/messagesHistory, newest first, in pages. The token is a channel id, global for the instance-wide channel, or dmu_<userId> for a direct message.
POST /api/chat/channels/:token/messagesSend. Body: body (up to 4,000 characters) and/or attachmentIds. A clientMsgId you generate makes a retry safe: the same id is never stored twice.
POST /api/chat/channels/:token/readMarks the channel read.
PATCH /api/chat/messages/:id, DELETEEdit or delete a message. Author, and moderators for delete.
GET /api/chat/voice/:idStreams a voice note. It needs the bearer token, and answers 410 once the audio is past its 7 days.

Search and history#

  • GET /api/search?q=...&limit=10 finds notes, tasks and areas the caller may see. The query needs at least 2 characters and the limit is at most 30. Guests get 403.
  • GET /api/me/history returns the caller's own timeline with limit (at most 100) and a before cursor taken from the previous answer's nextBefore.

Other endpoints#

  • GET /api/layers lists the map layers the caller may see, with bounds and order.
  • GET /api/version is public and reports the running build.
  • Administration lives under /api/admin/... and needs an Admin or User Admin; some calls need a full Admin (see Roles and permissions matrix). This includes users and roles, layers, activity, export and the integrations.
  • Integrations expose their own endpoints for machinery positions, panoramas, weather radar and monday.com webhooks; they are documented on their integration pages.

A small example#

  1. Sign in with POST /api/auth/login and keep the accessToken and the refresh token.
  2. Create an incident with POST /api/tasks, body { "kind": "INCIDENT", "title": "Blocked gate", "priority": "HIGH", "latitude": 52.37, "longitude": 4.9 }.
  3. Everyone who has that part of the map open sees it appear within a moment, through the live connection.
  4. Later, close it with PATCH /api/tasks/:id and { "status": "RESOLVED" }.

Something missing or unclear? Email info@anivizzion.nl.