Most/Docs

Location tracking

Most records where the participants of a conversation last were and who is sharing their location with the account, from what Messages.app writes: a position for every sent location and the start and stop of every location share. It answers through GET /v3/chats/{chat_id}/location, its history, three webhook events, the CLI, MCP and Most Desktop.

What Messages.app exposes, and what Most records#

A sent position — "Send My Current Location" in Messages — arrives as a `text/x-vlocation` card (`CL.loc.vcf`) whose Apple Maps link carries the coordinates. Most reads the card once, records one position for the sender's handle (latitude, longitude, the card's label and address when present, the map link, and the message time), and carries it on `message.received` as a `location` part instead of a media or contact part.

A location share — "Share My Location" — arrives as a start marker and, later, a stop marker (`item_type = 4` rows in chat.db). Most records the session: whose share it is (`inbound` when the counterparty shares with this account, `outbound` when this account shares with them), when it began, and when it ended. The moving points of a running share travel through Find My and are never written to chat.db, so Most does not claim to have them.

Both facts are persisted before anything is emitted, and persisted whether or not a webhook subscription matches, so the API answers for a conversation nobody subscribed to. Storage is idempotent on the Messages GUID of the row that produced the fact: a re-observed row records nothing and emits nothing.

Read where a conversation's participants are#

`GET /v3/chats/{chat_id}/location` answers the newest recorded position per handle and the running shares; `?include_ended=true` adds the sessions that already ended. `GET /v3/chats/{chat_id}/location/history` answers every recorded position newest first, optionally for one `handle`, from `since` (RFC 3339), at most `limit` (1 to 1000; 100 when omitted). Both take the same service bearer as every other `/v3/chats` route.

GET /v3/chats/{chat_id}/location
{
  "chat_id": "2f1c6a8e-9d51-4c4a-8b0f-3a9c1d2e4f5a",
  "positions": [
    {"id": "7a3f0d2c-…", "handle": "+15550001111", "direction": "inbound",
     "latitude": 52.229676, "longitude": 21.012229,
     "label": "Current Location", "address": null,
     "map_url": "http://maps.apple.com/?ll=52.229676,21.012229",
     "observed_at": "2026-09-21T10:00:00+00:00",
     "source": "messages-app-current-location"}
  ],
  "shares": [
    {"id": "9b8c7d6e-…", "handle": "+15550001111", "direction": "inbound",
     "began_at": "2026-09-21T10:01:00+00:00", "ended_at": null}
  ]
}

Refusals#

  • An unknown chat answers 404 with `detail: "the requested resource does not exist"`, the same refusal every `/v3/chats/{chat_id}` route gives.
  • `limit` outside 1 to 1000, or not a whole number, answers 400 with `detail: "limit must be a whole number from 1 to 1000"`.
  • `since` that is not RFC 3339 answers 400 with `detail: "since must be an RFC 3339 timestamp"`.
  • A conversation with no recorded position or share answers 200 with empty `positions` and `shares`; that is a fact about the conversation, not a failure.

Webhook events#

Subscribe to `location.received`, `location.sharing.started` and `location.sharing.stopped` exactly like the message events. Each delivery is signed the same way and carries `data.chat.id`, `data.handle.handle` and `data.direction`; `location.received` adds the coordinates, label, address, map link, `observed_at` and the `message_id` of the carrying message; the sharing events add `began_at` and, on stop, `ended_at`. `message.received` for a row carrying a sent location includes a `parts[]` entry of `type: "location"` with `latitude`, `longitude` and, when present, `label`, `address` and `map_url`.

location.received
{
  "id": "…", "type": "location.received", "created_at": "2026-09-21T10:00:00+00:00",
  "data": {
    "id": "7a3f0d2c-…", "chat": {"id": "2f1c6a8e-…"}, "handle": {"handle": "+15550001111"},
    "direction": "inbound", "message_id": "c4d5e6f7-…",
    "latitude": 52.229676, "longitude": 21.012229,
    "label": "Current Location", "address": null,
    "map_url": "http://maps.apple.com/?ll=52.229676,21.012229",
    "observed_at": "2026-09-21T10:00:00+00:00"
  }
}

CLI, MCP and desktop#

`most-cli location <chat_id> [--include-ended]` and `most-cli location-history <chat_id> [--handle <h>] [--since <rfc3339>] [--limit <n>]` perform the two reads and print the service's answer verbatim; a missing chat id is refused with `location requires a chat id` and exit status 2. The MCP tools `most_chat_location` and `most_chat_location_history` expose the same reads to an agent. Most Desktop's Location screen takes a chat id, shows the positions, the shares and the history, and opens a position's map link on click; a chat id that is not a UUID is refused before any request.

Read a conversation's location from the CLI
most-cli location 2f1c6a8e-9d51-4c4a-8b0f-3a9c1d2e4f5a
most-cli location 2f1c6a8e-9d51-4c4a-8b0f-3a9c1d2e4f5a --include-ended
most-cli location-history 2f1c6a8e-9d51-4c4a-8b0f-3a9c1d2e4f5a --handle +15550001111 --limit 50

Run the real test#

`make test-location` on a Mac builds the real `most-server`, points its `dev-mac` composition at a fixture Messages database through `MOST_CHAT_DB_PATH`, registers a webhook subscription to a listener the test owns, writes a sent location and a share start and stop the way Messages.app does, and asserts the four webhook deliveries, both endpoints and the refusals above. Commands, statuses, responses and server logs stay under `.wisent-output/location-evidence/`.