# ACM Destiny API — Full context for AI agents Tier: **client** (164 endpoints visible) API base: `https://www.acmdestiny.net/api/v1` Docs site: https://api.acmtrack.com This file is auto-generated from the live documentation and the OpenAPI specs filtered to the **client** tier. Higher-tier endpoints are not included. For full request/response schemas of any endpoint, fetch the machine-readable OpenAPI specs at https://api.acmtrack.com/openapi/ (one JSON per collection). --- ## Documentation ### intro # ACM Destiny API Welcome to the developer documentation for the **ACM Destiny API** — the REST interface to the ACM Destiny fleet management and telematics platform. These docs are split into two surfaces, which you can switch between from the top navigation: - **Dev Docs** (you are here) — concepts, authentication, conventions and step-by-step guides written for humans. - **[API Reference](/api/intro)** — the complete, machine-generated reference for every endpoint, with parameters, request bodies, response schemas and live code samples. ## Overview All API communication with Destiny is organised around REST. The API has predictable, resource-oriented URLs, accepts and returns **JSON**, and uses standard HTTP verbs and response codes. ``` https://www.acmdestiny.net/api/v1 ``` :::info Base URL Every endpoint is served under the `/api/v1` prefix on the ACM Destiny API host (`https://www.acmdestiny.net`). A full call therefore looks like `https://www.acmdestiny.net/api/v1/users/basic`. ::: ## The five API collections The Destiny surface is large — **464 endpoints** — and is grouped into five collections: | Collection | What it covers | | --- | --- | | **HTTP API** | The core platform: units, vehicles, clients, drivers, unit groups, commands, reports, dashboards, monitoring and more. | | **Admin API** | Administrative and platform-management operations. | | **Geo API** | Geographic services — geocoding, places and spatial lookups. | | **History Query API** | Historical telemetry and event queries over time ranges. | | **SSE** | Server-Sent Events guide for real-time streaming. | Browse them all in the **[API Reference](/api/intro)**. ## How to read these docs If you are integrating for the first time, follow this order: 1. **[Getting Started](/docs/getting-started)** — make your first authenticated call. 2. **[Authentication](/docs/authentication)** — obtain and refresh your bearer token. 3. **[Requests & Responses](/docs/requests-and-responses)** — formats, headers and conventions. 4. **[Errors](/docs/errors)** — how failures are reported. 5. **[Data Model](/docs/concepts/data-model)** — the difference between Units, Vehicles, Clients and more. :::note This site is generated from ACM's Destiny Postman collections. The API Reference is produced directly from those collections, so it reflects the real request and response shapes captured against a live Destiny server. ::: ### authentication # Authentication The Destiny API authenticates every request with a **bearer token** supplied in the `Authorization` HTTP header. ``` Authorization: bearer eyJ0eXAiOiJKV1... ``` ## Obtaining a token Log in with a username and password to receive a token. **`POST /api/v1/auth/login`** ```json { "username": "your-username", "password": "your-password" } ``` On success the API returns `200 OK`. The new authorization token is returned in the **`Authorization` response header** (it looks like `bearer eyJ0eXAiOiJKV1...`). The response body contains the authenticated user record. :::tip You can also retrieve the current token with **`GET /api/v1/auth/token`**, which returns `{ "data": { "token": "bearer token_key" } }`. ::: ## Using the token Cache the token and send it in the `Authorization` header on **every** subsequent request: ```bash curl https://www.acmdestiny.net/api/v1/tests/ping \ -H "Authorization: bearer eyJ0eXAiOiJKV1..." \ -H "Content-Type: application/json" ``` ## Token refresh — important :::warning Tokens rotate The token is **refreshed every few minutes** by the API server. Whenever you make a request, inspect the `Authorization` header on the **response**. If it contains a new token, **update your cache** and use the new token on all following requests. The previous token does not expire immediately — it remains valid for **7 days** — but you should always adopt the most recent token returned to you. ::: A robust client therefore: 1. Logs in once to get an initial token. 2. Stores the token. 3. Sends it on every request. 4. After every response, checks the `Authorization` header and, if present, overwrites the stored token with the new value. ## Permission checks Many resources are scoped to an organisational hierarchy (partner → agency → client). You can verify whether the logged-in user is allowed to perform an action before attempting it: **`GET /api/v1/auth/permission/{resource}/action/{action}`** For example, to check whether the user may `view` clients: ``` GET /api/v1/auth/permission/clients/action/view?partner_id=...&agency_id=...&client_id=... ``` ## Other auth endpoints | Endpoint | Purpose | | --- | --- | | `POST /api/v1/auth/logout` | End the current session. | | `POST /api/v1/auth/password/reset/request` | Request a password reset for an email address. | | `POST /api/v1/auth/password/reset` | Complete a password reset. | | `POST /api/v1/auth/register/complete` | Complete a new user's registration using a registration token. | | `POST /api/v1/auth/websentinel/user` | Authenticate a logged-in user with the WebSentinel service. | See the full list and exact shapes under **Authentication** in the **[API Reference](/api/intro)**. ### concepts/api-surface # API Surface The Destiny API spans **464 endpoints** across five collections. This page is a map; the **[API Reference](/api/intro)** has the detail. ## HTTP API The core platform. Major resource areas include: Authentication, Users, Units, Unit Groups, Unit Commands, Unit Swapping, Unit Transfer, Unit Safe Zones, Sensors, Clients, Vehicles, Vehicle Groups, Vehicle Expenses, Vehicle Expense Types, Vehicle Safe Zones, Drivers, Monitoring, Reports, Scheduled Reports, Dashboards, Event Profiles, Gateway Routes, API Bridges (Webfleet) and Admin support endpoints. A few patterns worth knowing: - **Form-data endpoints** — many resources expose `Get … Create Form Data` and `Get … Edit Form Data` endpoints. These return the option lists and field metadata needed to build create/edit forms in a UI. - **Listing variants** — resources commonly offer several list endpoints scoped by client, group, partner or agency (e.g. *Vehicles Listing By Client*, *…By Group*). Choose the most specific one for your context. - **Reports are asynchronous** — you `POST` to run a report, receive a report id, then poll `Get Report Status` / `Get Report By Id`. ## Admin API Administrative and platform-management operations (65 endpoints). ## Geo API Geographic services — geocoding, places and spatial lookups (44 endpoints). ## History Query API Historical telemetry and event queries over time ranges (52 endpoints). ## SSE Real-time streaming via Server-Sent Events. See the **[Streaming with SSE](/docs/guides/streaming-sse)** guide. :::note Endpoints are grouped in the reference by their collection and tag, mirroring the folders above, so you can navigate to the area you need quickly. ::: ### concepts/data-model # Data Model A handful of core concepts run through the entire Destiny API. Getting these right up front will save you a lot of confusion. ## Units vs Vehicles — the key distinction These are **not** the same thing, and the API keeps them separate on purpose: - A **Unit** is a physical tracking device (hardware) with its own identifiers — IMEI, serial number, type and model. - A **Vehicle** is the asset being tracked (a truck, car, trailer, asset). A Unit is **allocated** to a Vehicle. Hardware can be swapped, repaired, scrapped or transferred between vehicles and clients without losing the vehicle's history — which is why the API has a dedicated **Unit Swapping** and **Unit Transfer** set of operations, separate from creating or editing the vehicle itself. :::tip When you query telemetry, you can address it by **Unit** (`/units/.../telemetry`, or by IMEI / serial number) or by **Vehicle** (`/vehicles/.../telemetry`). Pick the one that matches what you actually hold a reference to. ::: ## Organisational hierarchy Records are scoped through an ownership hierarchy. Most list and permission endpoints accept `partner_id`, `agency_id` and/or `client_id` to scope results: ``` Partner ─▶ Agency ─▶ Client ─▶ (Vehicles, Units, Drivers, ...) ``` - **Partner** — top-level reseller / operator. - **Agency** — an optional layer beneath a partner. - **Client** — the end customer who owns vehicles and drivers. ## Grouping Both units and vehicles can be organised into groups for bulk operations and reporting: - **Unit Groups** — collections of units. - **Vehicle Groups** — collections of vehicles (used heavily by reports and status views). ## Other primary resources | Resource | Description | | --- | --- | | **Drivers** | People who operate vehicles; can be monitored and reported on. | | **Event Profiles** | Define which events are monitored per client/channel. | | **Reports** | Generated asynchronously (PDF) — you request a run, then poll for status. | | **Dashboards / Widgets** | User-configurable dashboard layouts. | | **Sensors** | Telemetry inputs, defined per unit type and model. | See the full set under the **[API Reference](/api/intro)**. ### errors # Errors Destiny uses conventional HTTP status codes. A `2xx` indicates success; other codes indicate a problem, with detail carried in the `warnings` and `warn_fields` keys of the response body. ## Status codes seen in the API | Code | Meaning | | --- | --- | | `200 OK` | The request succeeded. | | `400 Bad Request` | The request was malformed or could not be processed. | | `401 Unauthorized` | Authentication failed (e.g. wrong password) or the token is missing/invalid. | | `422 Unprocessable Entity` | The request was well-formed but a business rule rejected it. | | `425 Too Early` | The requested precondition was not yet met (e.g. unknown username during reset). | ## Error body shapes ### General problems — `warnings` ```json { "warnings": [ "Cannot assign unit as primary, a unit is already allocated in that slot." ] } ``` ### Field-level validation — `warn_fields` Each entry maps a field name to a human-readable message: ```json { "warn_fields": [ { "password": "An incorrect password was provided for the username: demo." } ] } ``` ## Handling errors A resilient client should: 1. Branch on the HTTP status code first. 2. On a non-`2xx`, read `warnings` for general messages and `warn_fields` for per-field validation errors. 3. Surface `warn_fields` messages next to the corresponding form inputs where relevant. :::tip For requests scoped to the partner / agency / client hierarchy, you can call the permission-check endpoint **before** acting to avoid predictable `401`/`422` responses. See **[Authentication → Permission checks](/docs/authentication#permission-checks)**. ::: ### getting-started # Getting Started This guide walks you through your first authenticated call to the Destiny API. ## 1. Confirm your host Every endpoint lives under the `/api/v1` prefix on the ACM Destiny API host: ``` https://www.acmdestiny.net/api/v1 ``` So a call to *Find User Basic Details* is `https://www.acmdestiny.net/api/v1/users/basic`. ## 2. Check connectivity The ping endpoint requires no authentication and confirms the server is reachable: ```bash curl https://www.acmdestiny.net/api/v1/tests/ping \ -H "Content-Type: application/json" ``` ```json { "data": { "message": "pong" } } ``` ## 3. Log in to get a token ```bash curl -X POST https://www.acmdestiny.net/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"your-username","password":"your-password"}' ``` The token is returned in the **`Authorization` response header**. Copy it for the next step. (See **[Authentication](/docs/authentication)** for the full token lifecycle, including refresh.) ## 4. Make your first authenticated request ```bash curl https://www.acmdestiny.net/api/v1/units \ -H "Authorization: bearer YOUR_TOKEN" \ -H "Content-Type: application/json" ``` ## 5. Explore the reference Every endpoint — with its parameters, request body, response schema and code samples in curl, Python, Node, PHP, C# and Java — is documented in the **[API Reference](/api/intro)**. :::tip Next steps - Learn the conventions in **[Requests & Responses](/docs/requests-and-responses)**. - Understand the **[Data Model](/docs/concepts/data-model)** (Units vs Vehicles is the key concept). - For real-time data, read the **[SSE streaming guide](/docs/guides/streaming-sse)**. ::: ### guides/forms-pattern # The Form-Data Pattern Most resources in Destiny that you can **create** or **edit** expose a companion endpoint that returns the data needed to build the form: the valid options, dropdown lists, and field metadata. You'll see these throughout the API as: - **`Get … Create Form Data`** — everything needed to render a *create* form. - **`Get … Edit Form Data`** — the same, pre-populated for an *edit* form. Examples: *Get Unit Create Form Data*, *Get Vehicle Edit Form Data*, *Get Client Create Form Data*, *Get Driver Create Form Data*. :::tip Looking for the actual endpoints? Every `Get … Create Form Data` / `Get … Edit Form Data` endpoint is listed under its resource in the **[API Reference](/api/intro)** (e.g. *HTTP API → Units → Get Unit Create Form Data*). This guide explains *why* they exist; the Reference is where you'll find each one's exact response shape. ::: ## Why it exists Many fields are references to other records (a unit type, a client, a vehicle group, a sensor model). Rather than hard-coding those option lists, you fetch them from the form-data endpoint so your UI always reflects what the server will accept. ## The flow ```text 1. GET …/create → form data (option lists, defaults, field metadata) 2. Build your form from that response 3. POST … → create the record using a valid selection ``` For edits: ```text 1. GET …/{id}/edit → current values + option lists 2. PUT …/{id} → submit the updated record ``` ## Example ```bash # 1. Fetch what a new unit needs curl "https://www.acmdestiny.net/api/v1/units/create" \ -H "Authorization: bearer YOUR_TOKEN" # 2. …render a form, let the user choose valid options… # 3. Create the unit curl -X POST "https://www.acmdestiny.net/api/v1/units" \ -H "Authorization: bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ /* fields chosen from the create-form options */ }' ``` :::tip Always drive your create/edit UIs from the form-data endpoint rather than assuming option lists. It keeps your integration correct as ACM adds unit types, sensors, expense types and so on. See **[Creating a Unit](/docs/guides/create-a-unit)** for a worked end-to-end example. ::: ### guides/geo-api-overview # Geo API — overview The **Geo API** handles everything spatial: turning coordinates into addresses, and managing **zones** (geofences) that you can use to trigger alerts when vehicles enter or leave defined areas. :::tip This guide is an orientation The **[Geo API in the Reference](/api/intro)** has every endpoint in this collection — reverse-geolocation, zone categories, and the three zone shapes — with parameters, payload schemas, and code samples. Browse it for anything not covered here. ::: ## What it contains (client-facing) | Capability | What it does | | --- | --- | | **Reverse geolocation** | `lat, lng` → human-readable address | | **Zone Categories (Client)** | Group your zones into named categories (e.g. "Depots", "Restricted") | | **Zones (Client)** | The geofences themselves — circular, rectangular, or polygonal | Each client has their own zones and zone categories. The "Global" variants of these endpoints are admin/staff-only and not visible to clients. ## Zones — three shapes You can create geofences in three shapes. Pick the one that matches what you're fencing: - **Circular** — a centre lat/lng + a radius. Simplest. Good for "around a depot." - **Rectangular** — two opposite corners. Good for rough quadrants. - **Polygonal** — an arbitrary list of lat/lng points. Use when the shape matters (a winding warehouse perimeter, a city border). ## Try it ```bash # Turn coordinates into an address curl "https://www.acmdestiny.net/api/v1/geo/reverse?lat=-26.10&lng=28.05" \ -H "Authorization: bearer YOUR_TOKEN" # List your zone categories curl https://www.acmdestiny.net/api/v1/geo/zone-categories/client \ -H "Authorization: bearer YOUR_TOKEN" # List your zones curl https://www.acmdestiny.net/api/v1/geo/zones/client \ -H "Authorization: bearer YOUR_TOKEN" ``` (The exact paths are listed in the [API Reference](/api/intro) under **Geo API** → *Reverse Geolocation*, *Zone Categories (Client)*, *Zones (Client)*. The reference is the source of truth for parameters and the request body shapes for creating zones.) ## How zones connect to alerts Creating a zone is just a definition. To get **alerted** when a vehicle enters or leaves a zone, you use **Vehicle Safe Zones** (or **Unit Safe Zones**) in the [HTTP API](/docs/guides/http-api-overview) to attach the zone to the vehicle — that wires up the alert behaviour. ## When to use a different collection - **Where is a specific vehicle right now?** → `GET /vehicles/{id}/telemetry` in the [HTTP API](/docs/guides/http-api-overview). - **Was this vehicle inside zone X last Tuesday?** → [History Query API](/docs/guides/history-api-overview) — query past positions and check against the zone yourself. - **Stream zone enter/leave events live** → [Streaming (SSE)](/docs/guides/streaming-sse). ### guides/history-api-overview # History Query API — overview The **History Query API** is for asking questions about the past — *last week*, *this month*, *between these dates*. If "live" or "right now" is in your question, you want a different collection. :::tip This guide is an orientation The **[History Query API in the Reference](/api/intro)** has every summary type, every history endpoint, and the exact path/query parameters for each. Browse it for the full surface — this guide only covers the patterns. ::: ## What it contains | Category | Use for | | --- | --- | | **Vehicle History / Unit History** | Raw point-by-point past data — every GPS fix, every event | | **Vehicle Summaries / Unit Summaries** | Aggregated views — totals, distances, counts | | **Vehicle Expenses** | Historical expense rollups | Summaries answer questions like *"how far did vehicle X drive in May?"* in one call. Raw history is the underlying detail — bigger, slower, but lets you reconstruct anything. ## Two ways to specify a time range Most endpoints accept the range in one of two forms: - **From/to** — explicit `from_date` + `to_date` (and often `from_time` / `to_time`). Use when you have specific bounds. - **Period** — a named relative window like `this-month`, `last-month`, `this-quarter`, plus a `date` anchor. Cleaner for "the usual" intervals. You'll see both in the [API Reference](/api/intro); pick whichever fits your UI. ## A "summary-type" for almost every metric Summary endpoints often take a `{summary-type}` path parameter — e.g. `speeding-distances`, `harsh-driving`, `fuel-events`, `idle-time`. The same endpoint shape returns different rollups depending on which type you pass. The exact list of valid types is in the endpoint description in the [API Reference](/api/intro). ## Try it ```bash # Vehicle summary for one vehicle over a named period curl "https://www.acmdestiny.net/api/v1/history/summary/vehicles/full/by-vehicle/{vehicle_id}/date/2026-05-01/period/this-month" \ -H "Authorization: bearer YOUR_TOKEN" # Raw history for one vehicle between two dates curl "https://www.acmdestiny.net/api/v1/history/vehicle/{vehicle_id}/from/2026-05-01/to/2026-05-07" \ -H "Authorization: bearer YOUR_TOKEN" ``` (Path shapes can vary slightly per endpoint — the [API Reference](/api/intro) under **History Query API** is authoritative.) ## When to use a different collection - **Right-now position / live status** → `GET /vehicles/{id}/telemetry` or `/monitoring/...` in the [HTTP API](/docs/guides/http-api-overview). - **Live stream as events happen** → [Streaming (SSE)](/docs/guides/streaming-sse). - **A generated PDF report** (debrief, expenditure, position log) → the **Reports** section of the [HTTP API](/docs/guides/http-api-overview). ### guides/http-api-overview # HTTP API — overview The **HTTP API** is the core of the Destiny platform. If you're reading, listing, creating, or updating something day-to-day, it's almost certainly here. The other collections (Geo, History, SSE) are specialised — most integrations spend 80% of their time in HTTP API. :::tip This guide is an orientation The **[HTTP API in the Reference](/api/intro)** has the complete list of endpoints in this collection — every parameter, request body, response schema, and a copy-paste code sample for each. If you can't find what you need below, browse the Reference: there's a lot more there. ::: ## What it contains | Resource | What it represents | | --- | --- | | **Units** | Physical tracking devices on vehicles or assets | | **Vehicles** | The asset being tracked (a truck, car, trailer) | | **Unit Groups / Vehicle Groups** | User-defined groupings for bulk operations and reporting | | **Drivers** | People assigned to vehicles | | **Clients** | The end customer who owns vehicles and drivers | | **Unit Commands** | Commands you can send to a unit (lock, immobilise, etc.) | | **Unit Safe Zones / Vehicle Safe Zones** | Per-vehicle/unit geofences | | **Monitoring** | Live "where is it right now" snapshots | | **Reports** | Generate PDFs (debrief, expenditure, position log, etc.) | | **Dashboards** | User-configurable dashboard widgets | | **Vehicle Expenses / Expense Types** | Fuel, maintenance, fees recorded against a vehicle | A **Unit ≠ a Vehicle**: a unit is the *hardware*, a vehicle is the *asset*. Units are *allocated* to vehicles. This is why you'll see telemetry endpoints addressed by unit *and* by vehicle. ## Patterns you'll see everywhere ### List vs find vs scoped listing Almost every resource has multiple ways to query it: - **`*/listing`** — a paged list of everything you can see. - **`find/*`** — search/filter by name or other criteria. - **`*/listing/by-client/{client_id}`**, **`by-group/{group_id}`**, **`by-partner/{partner_id}`** — same list, scoped to one parent. Use the most specific one for your context — it's faster and returns less data. ### The Form-Data pattern Anything with `Create` or `Edit` operations has a companion `Get * Create/Edit Form Data` endpoint that returns the option lists and field metadata needed to build the form. See [Form-Data Pattern](/docs/guides/forms-pattern) for why and how. ### Telemetry — four ways to ask You can fetch a unit's current telemetry by: - `GET /units/{id}/telemetry` (by id) - `GET /units/by-imei/{imei}/telemetry` - `GET /units/by-serial/{serial}/telemetry` - `GET /vehicles/{id}/telemetry` (resolves through the allocated unit) Pick whichever id you already hold. ## Try it Authenticate first (see [Getting Started](/docs/getting-started)), then: ```bash # List your units curl https://www.acmdestiny.net/api/v1/units/listing \ -H "Authorization: bearer YOUR_TOKEN" # Get a single vehicle curl https://www.acmdestiny.net/api/v1/vehicles/{id} \ -H "Authorization: bearer YOUR_TOKEN" # Live snapshot of a vehicle's primary unit (single GET — not a stream) curl https://www.acmdestiny.net/api/v1/monitoring/vehicle/{id}/primary \ -H "Authorization: bearer YOUR_TOKEN" ``` ## When to use a different collection - **Past data over a time range** → [History Query API](/docs/guides/history-api-overview). - **Lat/lng → address, or geofences** → [Geo API](/docs/guides/geo-api-overview). - **Live, ongoing telemetry stream** → [Streaming (SSE)](/docs/guides/streaming-sse). The full HTTP API surface — every endpoint, parameters and response shapes — is in the [API Reference](/api/intro) under **HTTP API**. ### guides/pagination # Pagination List endpoints (the various *Listing* and *Find* operations) return results in pages. A paged response carries the results in `data` and paging metadata in a `pagination` object alongside it. :::tip Looking for a specific endpoint? This guide explains the pattern. Every list endpoint with its exact query parameters and response shape lives in the **[API Reference](/api/intro)** — far more is available there than the guides cover. ::: ## Requesting a page Pass paging via query parameters. The common ones are: | Parameter | Type | Meaning | | --- | --- | --- | | `page` | integer | Which page to return (1-based). | | `per_page` | integer | Items per page. | | `limit` | integer | Maximum items to return. | ```bash curl "https://www.acmdestiny.net/api/v1/units/listing?page=2&per_page=50" \ -H "Authorization: bearer YOUR_TOKEN" ``` :::note Not every list endpoint accepts every parameter — check the specific operation in the **[API Reference](/api/intro)** for the exact query parameters it supports. The reference now types these parameters (e.g. `per_page` is an integer), so you can see at a glance what each one expects. ::: ## Reading the response ```json { "data": [ /* the page of results */ ], "pagination": { "page": 2, "per_page": 50, "total": 1234 } } ``` Use the `pagination` fields to decide whether to fetch the next page. Keep requesting until you've retrieved `total` items (or until a page returns fewer than `per_page` results). ## Tips - Prefer the **most specific** list endpoint for your context (e.g. *Units Listing By Client* over *Units Listing*) — it returns less data and is faster. - Keep `per_page` reasonable; very large pages are slower and heavier on both ends. ### guides/streaming-sse # Streaming with SSE (live telemetry) Instead of refreshing every few seconds to ask *"where is the vehicle now?"*, you open a single connection and the server **pushes** updates the moment they happen — GPS positions, trip events, anything live. That's Server-Sent Events (SSE). Think of it like a live news feed: you tune in once, and updates appear on screen as they're broadcast. No refresh, no polling, no delay. :::tip Looking for a specific endpoint? The subscription endpoints (`/api/v1/monitoring/unit/{id}`, `/api/v1/monitoring/vehicle/{id}/primary`) and everything else in the Monitoring + SSE collections are listed in the **[API Reference](/api/intro)** with full parameters and response shapes. ::: ## How it works — the mental model There are **three** things going on, in this order: ``` ┌─────────────┐ 1. open stream ┌──────────────┐ │ your code │ ─────────────────────────────► │ Destiny SSE │ │ (or browser)│ ◄──── stream_id event ──────── │ server │ └─────┬───────┘ └──────┬───────┘ │ │ │ 2. subscribe per vehicle/unit (REST POST) │ │ /api/v1/monitoring/unit/{id} │ │ ─────────────────────────────────────────────►│ │ │ │ ◄──── gps / event messages stream in ─────────│ │ ◄──── periodic "ping" event ──────────────────│ │ │ │ 3. answer the ping (REST POST) │ │ /sse/pong/{stream_id} │ │ ─────────────────────────────────────────────►│ ``` You open **one** SSE connection and can subscribe to many vehicles/units over it. The server sends a `stream_id` after you connect — you'll need it to subscribe and to answer pings. ## Try it in your terminal (5 minutes, real API) This uses the live API at `https://www.acmdestiny.net`. You'll need a Destiny username and password. ### 1. Get your bearer token The Destiny login is unusual: the token comes back in the **`Authorization` response *header***, not the body. The `-D -` flag below dumps the response headers so you can grab it. ```bash curl -D - -s -o /dev/null \ -X POST https://www.acmdestiny.net/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"YOUR_USERNAME","password":"YOUR_PASSWORD"}' \ | grep -i '^authorization:' ``` You'll see something like: ``` Authorization: bearer eyJ0eXAiOiJKV1... ``` Copy the long string after `bearer ` — that's `YOUR_TOKEN` for the next steps. > The token rotates every few minutes. In a real integration, read the > `Authorization` response header on every call and update your stored token > when a new one comes back (old ones stay valid for 7 days). ### 2. Find a unit you want to watch ```bash curl -s https://www.acmdestiny.net/api/v1/units/listing \ -H "Authorization: bearer YOUR_TOKEN" | head -c 800 ``` Pick a `unit_id` from the response. We'll call it `UNIT_ID` below. ### 3. Open the live stream — leave this terminal running ```bash curl --no-buffer -N \ -H "Authorization: bearer YOUR_TOKEN" \ https://www.acmdestiny.net/sse/connect ``` `--no-buffer -N` tells curl to flush output as data arrives. You should immediately see a `stream_id` event, then periodic `ping` events: ``` event: stream_id data: {"type":"stream_id","stream_id":"abcd1234..."} event: ping data: {"type":"ping"} ``` Copy the `stream_id`. ### 4. In a SECOND terminal, subscribe to that unit ```bash curl -X POST \ -H "Authorization: bearer YOUR_TOKEN" \ https://www.acmdestiny.net/api/v1/monitoring/unit/UNIT_ID ``` Within seconds, the **first terminal** starts receiving `gps` events as the unit reports its position: ``` event: gps data: {"type":"gps","telemetry":{"gps":{"latitude":-26.10,"longitude":28.05,...}}} ``` That's it — you're watching live telemetry stream in real time. ### 5. Keep the connection alive (ping/pong) The server sends a `ping` event **once every minute**. Your client has **10 seconds** to answer with a `pong` POST. If it doesn't, the server closes the SSE connection entirely — you can't just resume. You have to: 1. Open a brand-new SSE connection (back to step 3). 2. Re-subscribe to every unit/vehicle you were watching (back to step 4). In a real app the pong looks like: ```bash curl -X POST -H "Authorization: bearer YOUR_TOKEN" \ https://www.acmdestiny.net/sse/pong/STREAM_ID ``` …fired every time a `ping` arrives. ## Try it in your browser (DevTools console) Open any page on `acmdestiny.net` (so the session cookie is set), open **DevTools → Console**, and paste: ```js const base = 'https://www.acmdestiny.net'; const token = 'YOUR_TOKEN'; // from step 1 above const unitId = 1; // from step 2 const es = new EventSource(`${base}/sse/connect`); let streamId = null; es.onmessage = async (e) => { const msg = JSON.parse(e.data); if (msg.type === 'stream_id') { streamId = msg.stream_id; console.log('stream_id:', streamId); // now subscribe to the unit await fetch(`${base}/api/v1/monitoring/unit/${unitId}`, { headers: { Authorization: `bearer ${token}` }, }); } else if (msg.type === 'ping') { await fetch(`${base}/sse/pong/${streamId}`, { method: 'POST', headers: { Authorization: `bearer ${token}` }, }); } else if (msg.type === 'gps') { const g = msg.telemetry?.gps; console.log(`live: ${g.latitude}, ${g.longitude}`); } }; es.onerror = (err) => console.error('SSE error — browser will auto-reconnect', err); ``` Watch the console — every GPS update from that unit will print live. > **Important browser quirk:** the native `EventSource` API doesn't let you set > custom headers (so no `Authorization: bearer …` on the SSE call itself). The > snippet above relies on a session cookie already being set by `acmdestiny.net`. > If you need bearer auth on the stream from a non-cookie context, use a > fetch-based polyfill like > [`eventsource-polyfill`](https://www.npmjs.com/package/event-source-polyfill) > that supports custom headers, or pass the token as a query parameter (check the > [API Reference](/api/intro) for the exact pattern). ## Things to know before shipping it - **One connection, many subscriptions — but with a practical ceiling.** You don't need a separate SSE connection per vehicle — open one, subscribe to all the units/vehicles you care about over it. SSE is designed for **a single user watching a manageable set live** (a dispatcher with a map showing 5–50 vehicles, a control-room tab, a driver-app screen). It is **not the right tool for bulk live telemetry** across an entire fleet of hundreds or thousands of units — see "When SSE *isn't* the right tool" below. Browsers also cap you at 6 SSE connections per origin (shared across tabs). - **The token rotates.** Read the `Authorization` response header after every REST call and update your stored token. Old tokens stay valid for 7 days. - **Answer pings within 10 seconds.** The server sends a `ping` every minute and expects a `pong` POST within 10 seconds. Miss it once and the connection is closed — you'll need to open a new SSE connection *and* re-subscribe to every unit/vehicle. - **Browsers may freeze background tabs.** When a tab is inactive (or the user's computer goes to sleep), the browser can pause your JS — pings stop being answered and the server drops you. Detect the disconnect (`onerror`) and reconnect + re-subscribe when the tab becomes visible again (`document.visibilitychange`). - **It auto-reconnects in the browser.** The native `EventSource` retries on disconnect automatically. In Node or a server-side client, you may need to reconnect yourself (and re-subscribe to your units). - **SSE is one-way.** Subscriptions and pongs go through normal REST calls; only the event stream is push. ## When SSE *isn't* the right tool - **You want yesterday's trip** — use the [History Query API](/docs/guides/history-api-overview), not SSE. - **You want a snapshot right now** — a single `GET /units/{id}/telemetry` call is simpler than opening a stream. - **You want bulk live telemetry across hundreds or thousands of units at once** — that's outside SSE's design. Use **LDBS (Live Data Bridge Services)** instead. LDBS is a separate ACM service built specifically for high-volume, fleet-wide live data feeds (think: piping every event into your data warehouse, or running fleet-wide analytics in real time). Contact your ACM account manager to get an LDBS connection provisioned. Use SSE when you genuinely want **live, ongoing updates for a user watching a specific set of vehicles** — a dashboard map, a control room view, a real-time alert handler. Use **LDBS** for fleet-wide programmatic streaming. ## Not comfortable writing this yourself? Paste this URL into [Claude](https://claude.ai) or [Claude Code](https://claude.com/claude-code) along with what you want to build: ``` https://api.acmtrack.com/llms-full.txt ``` Example prompt: *"Here's the ACM Destiny API: https://api.acmtrack.com/llms-full.txt. I have a Destiny username and password and the ID of one of my vehicles. Write me a Python script that opens an SSE stream, subscribes to that vehicle, prints each GPS update with a timestamp, and answers pings automatically."* The AI gets the full API context (this guide included) and writes the integration for you. ### requests-and-responses # Requests & Responses ## Base URL ``` https://www.acmdestiny.net/api/v1 ``` ## Headers | Header | Value | When | | --- | --- | --- | | `Authorization` | `bearer ` | All authenticated requests. | | `Content-Type` | `application/json` | Requests with a JSON body. | ## Request bodies `POST`, `PUT` and `PATCH` requests send a single-level or nested **JSON** body: ```json { "username": "demo", "password": "••••••••" } ``` ## The response envelope Responses are JSON objects built from a consistent set of top-level keys. Not all keys appear on every response — they are included only when relevant. | Key | Type | Meaning | | --- | --- | --- | | `data` | object / array | The requested resource(s) or operation result. | | `notices` | array of strings | Informational messages about a successful operation. | | `pagination` | object | Paging metadata for list endpoints (page, totals, etc.). | | `warnings` | array of strings | Non-field problems with the request. | | `warn_fields` | array of objects | Field-level validation messages, keyed by field name. | | `debug` | array | Diagnostic detail (development environments). | ### Successful response ```json { "data": { "message": "pong" } } ``` ### Successful response with notices ```json { "notices": [ "A support email was successfully submitted to us, we will get back to you shortly." ] } ``` ## Pagination List endpoints return a `pagination` object alongside the `data` array. Use it to page through large result sets. Inspect any list endpoint in the **[API Reference](/api/intro)** to see the exact pagination fields it returns. ## HTTP verbs | Verb | Use | | --- | --- | | `GET` | Read a resource or list. | | `POST` | Create a resource, or run an action/report. | | `PUT` | Replace/update an existing resource. | | `PATCH` | Partially update a resource. | | `DELETE` | Remove a resource. | See **[Errors](/docs/errors)** for how failures are reported. --- ## Endpoint reference (164 endpoints, client tier) ### HTTP API (100 endpoints) #### Authentication - `GET /auth/token` — Get Authentication Token - `GET /auth/registration/some_token` — Request Submitted Registration - `POST /auth/login` — User Login - `POST /auth/logout` — User Logout - `POST /auth/websentinel/user` — WebSentinel Authentication - `POST /auth/pwd-reset-request` — Password Reset Request - `POST /auth/pwd-reset` — Perform a Password Reset - `POST /auth/registration` — Complete User Registration - `GET /auth/permission/clients/action/view` — User Permission Check #### Unit Groups - `GET /units/groups` — Find Unit Group - `GET /units/groups/listing` — Unit Group Listing - `GET /units/groups/listing/by-client/1` — Unit Group Listing By Client - `GET /units/groups/1` — Get a Unit Group - `GET /units/groups/create` — Get Unit Group Create Form Data - `GET /units/groups/1/edit` — Get Unit Group Edit Form Data #### Units - `GET /units` — Get a Unit - `GET /units#find-unit-s` — Find Unit(s) - `GET /units/by-type/sigfox/exclusive` — Find Units By Type - `GET /units/by-client` — Unit Listing By Client - `GET /units/by-vehicle/assigned` — Units Listing By Vehicle - `GET /units/by-vehicle/assigned-as/primary` — Get Unit By Assigned Slot - `GET /units/by-group/ids` — Unit Listing By Group - `GET /units/non-assigned/by-partner/1` — Units Listing By Partner - `GET /units/sensors/type/1/model/1` — Unit Sensors LIsting By Type And Model - `GET /units/sensors/type/1` — Unit Sensors Listing By Type - `GET /units/sensors` — Find Unit Sensors - `GET /units/listing` — Units Listing - `GET /units/telemetry` — Get Unit Telemetry - `GET /units/by-imei/telemetry` — Get Unit Telemetry by IMEI - `GET /units/by-serial-no/telemetry` — Get Unit Telemetry by Serial No. - `GET /units/view` — Units View - `GET /units/basic/edit` — Get Units Basic Edit Form Data - `GET /units/basic` — Get Unit Basic Information - `GET /units/create` — Get Unit Create Form Data - `GET /units/edit` — Get Unit Edit From Data - `GET /units/history` — Get Unit History - `GET /units/status/detailed` — Get Unit Comms Status (Detailed) - `GET /units/status/summary/clients` — Get Unit Comms Status Summary (Grouped by Client) - `GET /units/comms-status/summary/by-clients` — Get Unit Comms Status (By Client) - `GET /units/comms-status/summary/by-agencies/1,2,3` — Get Unit Comms Status (By Agency) - `GET /units/comms-status/summary/by-partners/1,2,3` — Get Unit Comms Status (By Partner) - `GET /units/comms-status/summary/totals` — Get Unit Comms Status Totals #### Clients - `GET /clients` — Find Client(s) - `GET /clients/listing/by-partner/1` — Clients Listing By Partner - `GET /clients/listing/by-agency/1` — Clients Listing By Agency - `GET /clients/listing/no-agency` — Clients Listing With No Agency - `GET /clients/listing` — Client Listing - `GET /clients/1/contacts` — Get Client Contacts By Id - `GET /clients/contacts` — Get Client Contacts - `GET /clients/contact-types` — Get Client Contact Types - `GET /clients/create` — Get Client Create Form Data - `GET /clients/1/edit` — Get Client Edit Form Data - `GET /clients#get-a-client` — Get a Client - `GET /clients#get-a-client-1` — Get a Client #### Vehicles - `GET /vehicles` — Find Vehicles - `GET /vehicles/listing` — Vehicles Listing - `GET /vehicles/create` — Get Vehicle Create Form Data - `GET /vehicles/listing/by-client` — Vehicles Option Listing By Client - `GET /vehicles/maintenance/by-client` — Vehicles Maintenance By Client - `GET /vehicles/maintenance/by-group` — Vehicles Maintenance By Group - `GET /vehicles/by-client/with-units` — Vehicles Listing By Client (With units) - `GET /vehicles/by-client` — Vehicles Listing By Client - `GET /vehicles/by-group/ids/with-units` — Vehicles Listing By Group (With Units) - `GET /vehicles/by-group/ids` — Vehicles Listing By Group - `GET /vehicles/with-units` — Vehicles Listing (With Units) - `GET /vehicles/edit` — Get Vehicle Edit Form Data - `GET /vehicles#get-a-vehicle` — Get a Vehicle - `GET /vehicles/primary/telemetry` — Get Vehicle Telemetry - `GET /vehicles/status/location/by-client` — Get Vehicle Location Status by Client - `GET /vehicles/status/location/by-vehicle-group` — Get Vehicle Location Status by Group - `GET /vehicles/status/trip/by-client` — Get Vehicle Trip Status by Client - `GET /vehicles/status/trip/by-vehicle-group` — Get Vehicle Trip Status by Group - `GET /vehicles/history` — Get Vehicle History #### Vehicle Groups - `GET /vehicles/groups/listing/by-client/1` — Vehicle Groups Listing By Client - `GET /vehicles/groups/by-client/1` — Get Vehicle Groups by Client - `GET /vehicles/groups/listing` — List Vehicle Groups - `GET /vehicles/groups/create` — Get Vehicle Groups Create Form Data - `GET /vehicles/groups/1/edit` — Get Vehicle Groups Edit Form Data - `GET /vehicles/groups/1` — Get Vehicle Group - `GET /vehicles/groups` — Find Vehicle Groups #### Vehicle Expenses - `GET /vehicles/expenses/by-client` — Find Vehicles Expenses - `GET /vehicles/expenses/create/client` — Get Vehicle Expense Create Form Data - `GET /vehicles/expenses/1/edit` — Get Vehicle Expense Edit Form Data - `GET /vehicles/expenses/1` — Get a Vehicle Expense #### Vehicle Expense Types - `GET /vehicles/expense/types/by-client` — Find Vehicles Expense Types Copy - `GET /vehicles/expense/types/by-client/listing` — Vehicle Expense Types Listing - `GET /vehicles/expense/types/create` — Get Vehicle Expense Type Create Form Data - `GET /vehicles/expense/types/1/edit` — Get Vehicle Expense Type Edit Form Data - `GET /vehicles/expense/types/1` — Get a Vehicle Expense Type #### Monitoring - `GET /monitoring/vehicle/primary` — Monitor Vehicle - `GET /monitoring/driver/1` — Monitor Driver - `GET /monitoring/unit` — Monitor Unit - `GET /monitoring/unit/by-imei` — Monitor Unit By IMEI - `GET /monitoring/unit/by-serial-no` — Monitor Unit By Serial No #### Drivers - `GET /drivers/listing` — Drivers Listing - `GET /drivers/listing/by-client/1` — Drivers Listing By Client - `GET /drivers/create` — Get Drivers Create Form Data - `GET /drivers/1/edit` — Get Drivers Edit Form Data - `GET /drivers/1` — Get A Driver - `GET /drivers` — Find Drivers ### Geo API (21 endpoints) #### Reverse Geolocation - `GET /geo/address/latitude/-25.834888/longitude/29.66106` — Get Address From Lat/Lng #### Zone Categories (Client) - `GET /geo/client/zones/categories/listing/by-client` — Zone Categories Listing - `GET /geo/client/zones/categories/create` — Get Zone Categories Create Form - `GET /geo/client/zones/categories/1/edit` — Get Zone Categories Edit Form - `GET /geo/client/zones/categories/1` — Get a Zone Category - `PUT /geo/client/zones/categories/1` — Update Existing Zone Category - `DELETE /geo/client/zones/categories/1` — Delete a Zone Category - `GET /geo/client/zones/categories/by-client` — Find Zone Categories - `POST /geo/client/zones/categories` — Add Zone Category #### Zones(Client) - `GET /geo/client/zones/listing/by-client` — Zones Listing - `GET /geo/client/zones/create` — Get Zone Create Form - `GET /geo/client/zones/1/edit` — Get Zone Edit Form - `GET /geo/client/zones/1192` — Get a Zone - `GET /geo/client/zones/by-client` — Find Zone - `POST /geo/client/zones` — Add Circular Zone - `POST /geo/client/zones#add-rectangular-zone` — Add Rectangular Zone - `POST /geo/client/zones#add-polygonal-zone` — Add Polygonal Zone - `PUT /geo/client/zones/1` — Update Existing Zone - `DELETE /geo/client/zones/1` — Delete a Zone - `POST /geo/client/zones/copy/from-client/to-client` — Copy Zones from One Client to Another - `POST /geo/client/zones/move/from-client/to-client` — Move Zones from One Client to Another ### History Query API (43 endpoints) #### Vehicle History - `GET /history/trips/vehicle/primary/date/all` — Get Vehicle Trip History (All) - `GET /history/trips/vehicle/primary/date/first` — Get Vehicle Trip History (First) - `GET /history/trips/vehicle/primary/date/last` — Get Vehicle Trip History (Last) - `GET /history/speeding/vehicle/primary/date` — Get Vehicle Speeding History - `GET /history/telemetry/vehicle/primary/date` — Get Vehicle Telemetry (All) - `GET /history/telemetry/vehicle/primary/period/from/time/to/time` — Get Vehicle Telemetry (Period) - `GET /history/trips/telemetry/vehicle/primary/date/from/to` — Get Vehicle Trip Telemetry (Time Range) - `GET /history/trips/telemetry/vehicle/primary/period/from/time/to/time` — Get Vehicle Trip Telemetry (Period) - `GET /history/trips/telemetry/vehicle/primary/date` — Get Vehicle Trip Telemetry By Date - `GET /history/telemetry/vehicle/primary/date/from/to` — Get Vehicle Telemetry (Time Range) - `GET /history/summary/vehicle/primary/from/to` — Get Vehicle Daily Summary - `GET /history/summary/vehicles/speeding-distances/by-client/date/period/this-month` — Get Full Vehicle Summary By Client Over Period - `GET /history/summary/vehicles/speeding-distances/by-client/date/period/this-month#get-vehicle-summary-by-type-and-by-client-over-period` — Get Vehicle Summary By Type and By Client Over Period - `GET /history/summary/vehicles/full/by-client/from/to` — Get Vehicle Summary From - To By Client - `GET /history/summary/vehicles/full/by-vehicle-group/client/from/to` — Get Vehicle Summary From - To By Vehicle Group #### Unit History - `GET /history/trips/unit/date/all` — Get Unit Trip History (All) - `GET /history/trips/unit/date/first` — Get Unit Trip History (First) - `GET /history/trips/unit/date/last` — Get Unit Trip History (Last) - `GET /history/telemetry/unit/date` — Get Unit Telemetry (All) - `GET /history/trips/telemetry/unit/date/from/to` — Get Unit Trip Telemetry (Time Range) - `GET /history/telemetry/unit/period/from/time/to/time` — Get Unit Telemetry (Period) - `GET /history/trips/telemetry/unit/period/from/time/to/time` — Get Unit Trip Telemetry (Period) - `GET /history/trips/telemetry/unit/date` — Get Unit Trip Telemetry By Date - `GET /history/telemetry/unit/date/from/to` — Get Unit Telemetry (Time Ranged) - `GET /history/speeding/unit/date` — Get Unit Speeding History - `GET /history/summary/unit/from/to` — Get Unit Daily Summary - `GET /history/summary/units/full/by-client/date/period/this-month` — Get Full Unit Summary By Client Over Period - `GET /history/summary/units/speeding-distances/by-client/date/period/this-month` — Get Unit Summary By Type and By Client Over Period - `GET /history/summary/units/full/by-client/from/to` — Get Unit Summary From - To By Client - `GET /history/summary/units/full/by-vehicle-group/client/from/to` — Get Unit Summary From - To By Unit Group - `GET /history/comms-history/units/by-client/from/to` — Get Unit Comms Status History (By Client) - `GET /history/comms-history/units/by-agency/from/to` — Get Unit Comms Status History (By Agency) - `GET /history/comms-history/units/by-partner/from/to` — Get Unit Comms Status History (By Partner) - `GET /history/comms-history/units/totals/from/to` — Get Unit Comms Status History Totals - `GET /history/comms-history/units/by-client/since/date/time` — Get Unit Comms Status History Since (By Client) - `GET /history/comms-history/units/by-agency/since/date/time` — Get Unit Comms Status History Since (By Agency) - `GET /history/comms-history/units/by-partner/since/date/time` — Get Unit Comms Status History Since (By Partner) - `GET /history/comms-history/units/totals/since/date/time` — Get Unit Comms Status History (Since) Totals #### Vehicle Summaries - `GET /history/summary/vehicles/distances/by-vehicle-group/1/client/date/period` — Get Vehicle Group Distances - `GET /history/summary/vehicles/distances/by-client/date/period` — Get Client Vehicles Distances #### Unit Summaries - `GET /history/summary/units/distances/by-client/date/period/this-month` — Get Client Unit Distances - `GET /history/summary/units/distances/by-unit-group/18/client/date/period` — Get Unit Group Distances #### Vehicle Expenses - `GET /history/vehicles/expenses/by-client/from/to` — Get Vehicle Expenses