API reference

Maps and flight watches

Live aircraft, flight history, saved maps and places, flight watches, notification groups and web push.

See Maps.

GET /v1/hooks/maps/shared/:token

A map shared by link, with its places. No authentication; owner and organization aren't included.

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  list: {
    places: {
      placeId: string
      listId: string
      name: string
      note?: string
      lat: number
      lon: number
      address?: string
      color?: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
      icon?: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
      createdAt: number
      updatedAt: number
    }[]
    listId: string
    name: string
    description?: string
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    sharing: "org" | "link" | "private"
    placeCount: number
    createdAt: number
    updatedAt: number
  }
}

GET /v1/hooks/maps/p/:token

An event's public position page (the link texts and emails give recipients outside the platform): the event's title, time, time zone and position, and until when the page follows the aircraft. No authentication; no watch, organization or recipient details. 410 once the link has expired (7 days).

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  label: string
  at: number
  timeZone: string
  lat: number
  lon: number
  lost: boolean
  liveUntil: number
  live: boolean
  expiresAt: number
}

GET /v1/hooks/maps/p/:token/live

Where that event's aircraft is now (position, altitude, speed, track), from the live picture, for 2 hours after the event: aircraft is null while it isn't seen, live: false after that. Only that one aircraft. No authentication; rate-limited per link.

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  live: false
  aircraft: null
} | {
  live: true
  aircraft: null
} | {
  live: true
  aircraft: {
    at: number
    lat: number
    lon: number
    alt: null | number
    gs: null | number
    track: null | number
    onGround: boolean
    icon: "unknown" | "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone" | "ground"
  }
}

GET /v1/hooks/maps/notify/:token

A notification recipient's status (pending, active, unsubscribed) and group name. No authentication: the token is the recipient's own.

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  status: "active" | "pending" | "unsubscribed"
  group: string
  email?: string
  phone?: string
}

POST /v1/hooks/maps/notify/:token/confirm

Confirms a recipient (double opt-in). No authentication.

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  status: "active" | "pending" | "unsubscribed"
  group: string
  email?: string
  phone?: string
}

POST /v1/hooks/maps/notify/:token/unsubscribe

Unsubscribes a recipient (RFC 8058 one-click: mail clients post here from the List-Unsubscribe header). No authentication.

Auth: none

Path parameterDescription
:tokenA secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed.

Response 200

{
  status: "active" | "pending" | "unsubscribed"
  group: string
  email?: string
  phone?: string
}

POST /v1/hooks/maps/location

A background location report from the Maps app with the phone's key (Authorization: Bearer si_loc_…; the body as for …/report). The key writes only its phone's fix and works only while that phone's sign-in does: 401 otherwise, and the phone's location is deleted. 204 when dropped (one report per 20 s is stored).

Auth: none

Response 200

{
  ok: true
  validUntil?: number
}

Response 204 with no body.

Errors

StatusMessage
400Send JSON.
401Unauthorized

GET /v1/orgs/:orgId/maps/config

What the apps need to draw: the ADS-B source and its credit line, preset areas, event types, channels (mobile, web, email, sms, call: available or not, and their labels), what a new watch gets by default for the caller (defaults: events, channels, smsEvents), the Mapbox public token for the native map with the Referer it may need (mapbox), and for texts whether the SMS account is still in the provider's sandbox and where the caller's own texts go (masked). For circling and approach alerts: aircraft classes, enabled aircraft presets, the org's locations, circling sensitivity presets and approach defaults. units: the caller's units for measurements (the org's setting with their own on top; /settings).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  provider: {
    id: string
    label: string
    attribution: string
    terms: string
    lookups: {
      registration: boolean
      callsign: boolean
      type: boolean
      hexBatch: number
    }
  }
  providers: ["adsblol", "adsbfi", "airplaneslive", "opensky", "adsbexchange"]
  sources: {
    id: string
    label: string
    attribution: string
    use: "all" | "last_resort"
  }[]
  areas: {
    id: string
    label: string
    source: string
  }[]
  events: {
    needsPoint?: true
    type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead"
    label: string
  }[]
  aircraftClasses: {
    id: "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone"
    label: string
  }[]
  presets: [] | {
    id: string
    label: string
  }[]
  locations: {
    locationId: string
    name: string
    lat: number
    lon: number
    siteRadiusM: number
  }[]
  circling: {
    sensitive: {
      turns: 0.75
      radiusKm: 3
      windowMinutes: 10
    }
    normal: {
      turns: 1
      radiusKm: 2
      windowMinutes: 8
    }
    strict: {
      turns: 2
      radiusKm: 1.5
      windowMinutes: 8
    }
  }
  approachDefaults: {
    lookaheadMinutes: number
    minClosingKt: number
    maxAltitudeFt: null | number
    overheadKm: number
  }
  deliveryLabels: {
    failed: string
    sent: string
    duplicate: string
    quiet: string
    cooldown: string
    no_recipients: string
  }
  channels: {
    mobile: true
    web: true
    email: boolean
    sms: boolean
    call: boolean
  }
  channelLabels: {
    [key: string]: string
  }
  defaults: {
    events: {
      emergency: boolean
      takeoff: boolean
      landing: boolean
      signal_lost: boolean
      signal_resumed: boolean
      area_enter: boolean
      area_leave: boolean
      orbit: boolean
      road_follow: boolean
      road_switch: boolean
      road_repeat: boolean
      road_leave: boolean
      interchange: boolean
      threshold: boolean
      approach: boolean
      overhead: boolean
    }
    channels: ("email" | "sms" | "call" | "mobile" | "web")[]
    smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
  }
  mapbox: {
    token: null | string
    referer: null | string
  }
  sms: {
    sandbox: boolean | null
    phone: null | string
    phoneSettings: null | string
  }
  units: {
    speed: string
    altitude: string
    distance: string
  }
}

GET /v1/orgs/:orgId/maps/settings

Maps settings: the units measurements show in (speed kt | km/h | mph, altitude ft | m, distance nm | km | mi). units is what the caller sees, org.units the org's setting and mine.units the caller's own (each only what it sets; unset is knots, feet, nautical miles).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  units: {
    speed: "kt" | "km/h" | "mph"
    altitude: "m" | "ft"
    distance: "nm" | "km" | "mi"
  }
  org: {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  mine: null | {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  canEditOrg: boolean
}

PUT /v1/orgs/:orgId/maps/settings

The org's units, for everyone without their own (org owners and admins, or a key, with maps:write). A unit left out goes back to the default.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  units: {
    speed: "kt" | "km/h" | "mph"
    altitude: "m" | "ft"
    distance: "nm" | "km" | "mi"
  }
  org: {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  mine: null | {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  canEditOrg: boolean
}

Errors

StatusMessage
400Send JSON.

PUT /v1/orgs/:orgId/maps/settings/me

The caller's own units, in every org, over the org's ({ units: null } follows the org again). People only.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  units: {
    speed: "kt" | "km/h" | "mph"
    altitude: "m" | "ft"
    distance: "nm" | "km" | "mi"
  }
  org: {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  mine: null | {
    units: {
      speed?: "kt" | "km/h" | "mph"
      altitude?: "m" | "ft"
      distance?: "nm" | "km" | "mi"
    }
  }
  canEditOrg: boolean
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/aircraft

Live aircraft inside bbox (west,south,east,north), from the flight poller's latest snapshot: position, barometric altitude (null on the ground), ground speed, track, vertical rate, callsign, registration, type, squawk, icon class and, with trails=1, the last 15 minutes of track. Also returns the source, its credit line and the areas polled. Views outside the polled areas are added to the poller's areas for a few minutes.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredNotes
bboxstringNo
trailsstringNo

Response 200

{
  issues?: string[]
  pending: boolean
  at: null
  provider: null
  coverage: []
  aircraft: []
  sources?: undefined
} | {
  issues?: string[]
  pending: boolean
  at: number
  provider: {
    label: string
    id: string
    attribution: string
  }
  sources?: {
    label: string
    id: string
    attribution: string
  }[]
  coverage: {
    lat: number
    lon: number
    radiusNm: number
  }[]
  aircraft: {
    type?: string
    at: number
    hex: string
    source?: string
    desc?: string
    lat: number
    lon: number
    alt: null | number
    onGround: boolean
    altGeom?: number
    gs?: number
    track?: number
    vs?: number
    callsign?: string
    registration?: string
    category?: string
    squawk?: string
    emergency?: string
    icon: "unknown" | "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone" | "ground"
    operator?: string
    road?: string
    trail?: ([number, number, number, null | number, null | number, null | number])[]
  }[]
}

Errors

StatusMessage
400bbox is west,south,east,north in degrees.

GET /v1/orgs/:orgId/maps/aircraft/nearest

The nearest aircraft in the air to a point, and the next two (the phone's nearest-aircraft widget): which way each is from the point (bearingDeg, compass), how far (rangeM), its altitude (ft), speed (kt), track and phase, and, while it descends, when it should land (landing.at, unix seconds) and at which airport when one lies ahead on its track. The point is rounded to about 100 m and never stored or logged; like a map view, an area outside the polled ones is covered for a few minutes from now (pending until it is). units: the caller's Maps units, for showing the numbers.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredDefaultNotes
latnumberYes-90–90; coerced from a string
lonnumberYes-180–180; coerced from a string
radiusKmnumberNo501–200; coerced from a string

Response 200

{
  units: {
    speed: string
    altitude: string
    distance: string
  }
  radiusKm: number
  aircraft: {
    hex: string
    callsign?: string
    registration?: string
    type?: string
    desc?: string
    operator?: string
    icon: "unknown" | "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone" | "ground"
    at: number
    rangeM: number
    bearingDeg: number
    compass: "S" | "N" | "NE" | "E" | "SE" | "SW" | "W" | "NW"
    alt: null | number
    gs?: number
    track?: number
    vs?: number
    phase: "lost" | "taxi" | "climbing" | "cruise" | "descending" | "landed"
    landing: null | {
      at: number
      airport: null | {
        name: string
        code?: string
      }
    }
  }[]
  issues?: string[]
  at: null | number
  provider: null | {
    label: string
    id: string
    attribution: string
  }
  sources?: {
    label: string
    id: string
    attribution: string
  }[]
  pending: boolean
}

Aircraft in the live snapshot by ICAO hex, registration or callsign (q; exact matches first, then prefixes).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredDefaultNotes
qstringNo""

Response 200

{
  aircraft: {
    trail?: undefined
    type?: string
    at: number
    hex: string
    source?: string
    desc?: string
    lat: number
    lon: number
    alt: null | number
    onGround: boolean
    altGeom?: number
    gs?: number
    track?: number
    vs?: number
    callsign?: string
    registration?: string
    category?: string
    squawk?: string
    emergency?: string
    icon: "unknown" | "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone" | "ground"
    operator?: string
    road?: string
  }[]
}

GET /v1/orgs/:orgId/maps/aircraft/:hex

One aircraft wherever it is, with its recent track: live from the snapshot (live: true), else its last known position and the track before it from recorded positions (live: false, lastSeen), or aircraft: null when nothing is known. When it isn't live the poller looks it up by hex for a few minutes (pending).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:hexICAO 24-bit aircraft address, 6 hex digits (7c6b2d).

Response 200

{
  at: null | number
  provider: null | {
    label: string
    id: string
    attribution: string
  }
  aircraft: null | {
    type?: string
    at: number
    hex: string
    source?: string
    desc?: string
    lat: number
    lon: number
    alt: null | number
    onGround: boolean
    altGeom?: number
    gs?: number
    track?: number
    vs?: number
    callsign?: string
    registration?: string
    category?: string
    squawk?: string
    emergency?: string
    icon: "unknown" | "helicopter" | "heavy" | "jet" | "turboprop" | "light" | "glider" | "balloon" | "drone" | "ground"
    operator?: string
    road?: string
    trail?: ([number, number, number, null | number, null | number, null | number])[]
  }
  live: boolean
  lastSeen: null | number
  pending?: boolean
}

Errors

StatusMessage
404Aircraft not found

GET /v1/orgs/:orgId/maps/aircraft/:hex/track

Recorded track of a watched aircraft over the last hours (0.25–24, default 6): points [seconds, lon, lat, altitude ft | null, ground speed kt, track°], oldest first. Positions are recorded only while a watch covers the aircraft (kept 14 days).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:hexICAO 24-bit aircraft address, 6 hex digits (7c6b2d).
Query parameterTypeRequiredDefaultNotes
hoursstringNo6

Response 200

{
  points: ([number, number, number, null | number, null | number, null | number])[]
  distanceNm: number
  recorded: boolean
  registration?: string
  callsign?: string
  type?: string
  hex: string
  from: number
  to: number
}

Errors

StatusMessage
404Aircraft not found

GET /v1/orgs/:orgId/maps/lists

Saved maps the caller can see: their own, and those shared with the organization or by link. With places=1, each map includes its places.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredNotes
placesstringNo

Response 200

{
  lists: {
    listId: string
    orgId: string
    ownerId: string
    name: string
    description?: string
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    sharing: "org" | "link" | "private"
    shareToken?: string
    placeCount: number
    createdAt: number
    updatedAt: number
    places?: {
      placeId: string
      listId: string
      name: string
      note?: string
      lat: number
      lon: number
      address?: string
      color?: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
      icon?: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
      createdBy: string
      createdAt: number
      updatedAt: number
    }[]
  }[]
}

POST /v1/orgs/:orgId/maps/lists

Creates a saved map: name, optional description, pin color (chart-1…chart-5, foreground), icon and sharing (private, org, link).

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  list: {
    listId: string
    orgId: string
    ownerId: string
    name: string
    description?: string
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    sharing: "org" | "link" | "private"
    shareToken?: string
    placeCount: number
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/lists/:listId

A saved map with its places.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).

Response 200

{
  list: {
    places: {
      placeId: string
      listId: string
      name: string
      note?: string
      lat: number
      lon: number
      address?: string
      color?: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
      icon?: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
      createdBy: string
      createdAt: number
      updatedAt: number
    }[]
    listId: string
    orgId: string
    ownerId: string
    name: string
    description?: string
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    sharing: "org" | "link" | "private"
    shareToken?: string
    placeCount: number
    createdAt: number
    updatedAt: number
  }
}

PATCH /v1/orgs/:orgId/maps/lists/:listId

Renames a map or changes its description, pin style or sharing. sharing: "link" returns a shareToken for the public link; rotateLink: true replaces it; any other sharing turns the link off. Owner, or an org admin for shared maps.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).

Response 200

{
  list: {
    listId: string
    orgId: string
    ownerId: string
    name: string
    description?: string
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    sharing: "org" | "link" | "private"
    shareToken?: string
    placeCount: number
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

DELETE /v1/orgs/:orgId/maps/lists/:listId

Deletes a map and its places.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).

Response 200

{
  ok: true
}

POST /v1/orgs/:orgId/maps/lists/:listId/places

Adds a place: name, lat, lon, optional note, address, and its own pin color and icon.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).

Response 201

{
  place: {
    placeId: string
    listId: string
    name: string
    note?: string
    lat: number
    lon: number
    address?: string
    color?: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon?: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    createdBy: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

PATCH /v1/orgs/:orgId/maps/lists/:listId/places/:placeId

Changes a place; moveTo moves it to another of your maps.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).
:placeIdPlace id (plc_…).

Response 200

{
  place: {
    placeId: string
    listId: string
    name: string
    note?: string
    lat: number
    lon: number
    address?: string
    color?: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    icon?: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    createdBy: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

DELETE /v1/orgs/:orgId/maps/lists/:listId/places/:placeId

Removes a place.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:listIdSaved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…).
:placeIdPlace id (plc_…).

Response 200

{
  ok: true
}

GET /v1/orgs/:orgId/maps/watches

Flight watches the caller can see (their own and those shared with the organization).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  watches: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }[]
}

POST /v1/orgs/:orgId/maps/watches

Creates a flight watch: name, targets ({ kind: hex | registration | callsign | type, value }, a trailing * matches prefixes; no_callsign, any (value *) and class (helicopter, light, turboprop, jet, heavy, glider, balloon, drone) match inside areas only and need one; preset names an aircraft preset), exclude (the same shape: aircraft matching any never match, e.g. callsign POL3* except POL31), areas ({ kind: "preset", id: "au-vic" }, circles { center: [lon, lat], radiusKm }, polygons { ring: [[lon, lat], …] }, an org location { kind: "location", locationId, radiusKm } or { kind: "near_me", radiusKm } around the owner's phone, which makes the watch personal: private, the owner's only, no groups or people), limitToAreas, events (on/off per event type; approach and overhead need a location or near-me area), settings (signal, orbit with maxAltitudeFt, loiterMinutes and ignoreAirportsKm, approach, roads, thresholds), notify (channels: mobile, web, email, sms, call, with the older push read as mobile and web; includeOwner, groupIds, userIds (org members), smsEvents, smsFormat, smsPrefix, cooldownMinutes, timeZone, quietHours), identity (mode: any or pin_first; pinnedHex), lifetime (startsAt, endsAt or forHours, maxNotifications, maxFlights, schedule with days, start, end, timeZone) and log (enabled, retentionDays, null for forever). Type targets match inside the watch's areas. Left out, every event is on and channels are the caller's defaults (GET …/maps/config → defaults). The maker gets a confirmation by push when they're a recipient. See Maps → Flight watches for every setting and its default.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/watches/:watchId

A flight watch with every setting, defaults filled in. A near-me watch without a current location reads status: "waiting_location".

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

PATCH /v1/orgs/:orgId/maps/watches/:watchId

Changes a watch: fields sent replace the stored ones (nested objects whole). Owner, or an org admin for shared watches.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

Errors

StatusMessage
400Send JSON.

POST /v1/orgs/:orgId/maps/watches/:watchId/pause

Pauses a watch: the poller stops evaluating it and nothing is sent.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

POST /v1/orgs/:orgId/maps/watches/:watchId/resume

Resumes a paused watch.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

POST /v1/orgs/:orgId/maps/watches/:watchId/restart

Starts an ended watch again (it ended at its end time, or after its notification or flight limit) with its counters at zero. A watch whose end time has passed needs a new one (or none) first.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  watch: {
    name: string
    visibility: "org" | "private"
    targets: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    exclude: {
      kind: "any" | "type" | "hex" | "callsign" | "registration" | "preset" | "no_callsign" | "class"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    }[]
    limitToAreas: boolean
    events: {
      emergency?: boolean
      takeoff?: boolean
      landing?: boolean
      signal_lost?: boolean
      signal_resumed?: boolean
      area_enter?: boolean
      area_leave?: boolean
      orbit?: boolean
      road_follow?: boolean
      road_switch?: boolean
      road_repeat?: boolean
      road_leave?: boolean
      interchange?: boolean
      threshold?: boolean
      approach?: boolean
      overhead?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: number
        maxAltitudeFt: null | number
        loiterMinutes: null | number
        ignoreAirportsKm: number
      }
      approach: {
        lookaheadMinutes: number
        minClosingKt: number
        maxAltitudeFt: null | number
        overheadKm: number
      }
      roads: {
        corridorM: number
        headingToleranceDeg: number
        minSeconds: number
        minShare: number
        repeatMinutes: number
        endAfterSeconds: number
        maxAltitudeFt: number
        minSpeedKt: number
        classes: ("primary" | "motorway" | "trunk")[]
      }
      thresholds: {
        altitudeAboveFt?: number
        altitudeBelowFt?: number
        speedAboveKt?: number
        speedBelowKt?: number
        climbAboveFpm?: number
        descentAboveFpm?: number
        squawks?: string[]
      }
    }
    notify: {
      membershipNotices: boolean
      liveActivity: boolean
      smsEvents: null | ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      cooldownMinutes: number
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allow: ("emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead")[]
      }
      timeZone?: string
    }
    identity: {
      mode: "any" | "pin_first"
      pinnedHex: null | string
    }
    lifetime: {
      startsAt: null | number
      endsAt: null | number
      maxNotifications: null | number
      maxFlights: null | number
      schedule: null | {
        days: number[]
        start: string
        end: string
        timeZone: string
      }
      forHours?: number
    }
    log: {
      enabled: boolean
      retentionDays: null | number
    }
    paused: boolean
    watchId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    notificationsSent: number
    flightsCompleted: number
    endedAt?: number
    endedReason?: "time" | "notifications" | "flights"
    personal?: boolean
    status: "active" | "waiting" | "paused" | "ended" | "off_schedule" | "waiting_location"
  }
}

POST /v1/orgs/:orgId/maps/watches/:watchId/test

Sends the caller a sample alert on the watch's channels that reach them and returns what each did: mobile (devices: phones with the Maps app, queued), web (sent, devices), email (sent), sms (sent, or skipped with why) and call (skipped). People only; maps:write, or the watch's owner.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  mobile?: {
    devices: number
    queued: boolean
  }
  web?: {
    sent: number
    devices: number
  }
  email?: {
    sent?: boolean
    skipped?: string
  }
  sms?: {
    sent?: boolean
    error?: string
    skipped?: string
  }
  call?: {
    skipped: string
  }
}

GET /v1/orgs/:orgId/maps/watches/:watchId/flights

Flights the watch logged, newest first: airframe (hex, registration, callsign, type), start (takeoff or first_seen) and end (landing, lost, left, watch_ended, schedule), status (in_progress, completed, ended), distance, highest altitude, events and roads followed. hex filters to one airframe; pass the returned cursor for older ones.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).
Query parameterTypeRequiredDefaultNotes
limitstringNo50
hexstringNo
cursorstringNo

Response 200

{
  flights: {
    flightId: string
    watchId: string
    hex: string
    registration?: string
    callsign?: string
    type?: string
    desc?: string
    status: "in_progress" | "completed" | "ended"
    endReason?: string
    startedAt: number
    endedAt?: number
    startedBy: "takeoff" | "first_seen"
    start: {
      lat: number
      lon: number
      alt: null | number
    }
    end?: {
      lat: number
      lon: number
      alt: null | number
    }
    maxAlt: null | number
    distanceNm: number
    events: {
      type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead"
      at: number
      title: string
      text: string
    }[]
    roads: {
      label: string
      from: number
      to?: number
    }[]
    points?: ([number, number, number, null | number, null | number, null | number])[]
  }[]
  cursor: null | string
}

GET /v1/orgs/:orgId/maps/watches/:watchId/flights/:flightId

One logged flight with its track (points: [seconds, lon, lat, altitude ft | null, ground speed kt, track°]).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).
:flightIdLogged flight id (fgl_…).

Response 200

{
  flight: {
    flightId: string
    watchId: string
    hex: string
    registration?: string
    callsign?: string
    type?: string
    desc?: string
    status: "in_progress" | "completed" | "ended"
    endReason?: string
    startedAt: number
    endedAt?: number
    startedBy: "takeoff" | "first_seen"
    start: {
      lat: number
      lon: number
      alt: null | number
    }
    end?: {
      lat: number
      lon: number
      alt: null | number
    }
    maxAlt: null | number
    distanceNm: number
    events: {
      type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead"
      at: number
      title: string
      text: string
    }[]
    roads: {
      label: string
      from: number
      to?: number
    }[]
    points?: ([number, number, number, null | number, null | number, null | number])[]
  }
}

GET /v1/orgs/:orgId/maps/watches/:watchId/flights/:flightId/export

A logged flight as a file: format=gpx (track and events as waypoints), kml (line and time-stamped track) or csv (one row per position, events alongside).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).
:flightIdLogged flight id (fgl_…).
Query parameterTypeRequiredDefaultNotes
formatstringNo"gpx"

Response 200 with no body.

Errors

StatusMessage
400format is gpx, kml or csv.

DELETE /v1/orgs/:orgId/maps/watches/:watchId

Deletes a watch and its flight logs. Its events expire after 30 days.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).

Response 200

{
  ok: true
}

GET /v1/orgs/:orgId/maps/watches/:watchId/events

A watch's events, newest first (kept 30 days): type, aircraft, position, text, and what happened to the notification (sent with channels, quiet, no_recipients, failed). Pass the returned cursor for older ones.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:watchIdFlight watch id (fwt_…).
Query parameterTypeRequiredDefaultNotes
limitstringNo50
cursorstringNo

Response 200

{
  events: {
    eventId: string
    watchId: string
    type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead" | "recipients_added" | "recipients_removed" | "watch_started"
    at: number
    hex: string
    title: string
    text: string
    aircraft: {
      registration?: string
      callsign?: string
      type?: string
      desc?: string
    }
    position?: {
      lat: number
      lon: number
      alt: null | number
      gs?: number
      vs?: number
      track?: number
    }
    data?: {
      [key: string]: unknown
    }
    delivery?: {
      status: "failed" | "sent" | "duplicate" | "quiet" | "cooldown" | "no_recipients"
      channels?: ("email" | "sms" | "call" | "mobile" | "push" | "web")[]
      recipients?: number
      errors?: string[]
      duplicates?: {
        recipients: number
        of: string[]
      }
    }
  }[]
  cursor: null | string
}

GET /v1/orgs/:orgId/maps/events

Recent events across the watches the caller can see, newest first.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredDefaultNotes
limitstringNo50

Response 200

{
  events: {
    eventId: string
    watchId: string
    type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "interchange" | "threshold" | "approach" | "overhead" | "recipients_added" | "recipients_removed" | "watch_started"
    at: number
    hex: string
    title: string
    text: string
    aircraft: {
      registration?: string
      callsign?: string
      type?: string
      desc?: string
    }
    position?: {
      lat: number
      lon: number
      alt: null | number
      gs?: number
      vs?: number
      track?: number
    }
    data?: {
      [key: string]: unknown
    }
    delivery?: {
      status: "failed" | "sent" | "duplicate" | "quiet" | "cooldown" | "no_recipients"
      channels?: ("email" | "sms" | "call" | "mobile" | "push" | "web")[]
      recipients?: number
      errors?: string[]
      duplicates?: {
        recipients: number
        of: string[]
      }
    }
  }[]
}

GET /v1/orgs/:orgId/maps/incidents

Emergency incidents and warnings (Australia), from the incidents poller's picture: filtered to a view (bbox), hazards (hazards=bushfire,flood) and a minimum level (minLevel); sorted by distance from near=lon,lat (adds distanceKm), else most severe first; areas=0 leaves out warning areas (lists); limit. feeds carries each feed's credit line and licence. Answers 304 to a matching If-None-Match.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  updatedAt: null | number
  feeds: {
    sourceId: string
    label: string
    state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU"
    attribution: string
    licence: string
    licenceUrl?: string
    homepage?: string
    count: number
    ok: boolean
    checkedAt?: number
    changedAt?: number
    error?: string
  }[]
  incidents: {
    id: string
    sourceId: string
    state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    kind: "warning" | "incident"
    hazard: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
    level: "none" | "advice" | "watch_and_act" | "emergency_warning"
    status?: string
    statusClass: "unknown" | "active" | "contained" | "controlled"
    title: string
    category?: string
    location?: string
    lga?: string
    action?: string
    description?: string
    size?: string
    agency?: string
    resources?: number
    updated: number
    published?: number
    url?: string
    point: [number, number]
    polygons?: [number, number][][][]
    distanceKm?: number
  }[]
  total: number
  etag: string
}

Response 304 with no body.

GET /v1/orgs/:orgId/maps/incidents/feeds

The emergency feeds in use: credit lines, licences, when each was last read and whether it's working.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  updatedAt: null | number
  feeds: {
    sourceId: string
    label: string
    state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU"
    attribution: string
    licence: string
    licenceUrl?: string
    homepage?: string
    count: number
    ok: boolean
    checkedAt?: number
    changedAt?: number
    error?: string
  }[]
}

GET /v1/orgs/:orgId/maps/incidents/:incidentId

One incident (or its last version if it has ended) with its history of changes, newest first.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:incidentIdIncident id as listed (<feed>:<the agency's id>, e.g. vic:ESTA:261009192), URL-encoded.

Response 200

{
  incident: null | {
    id: string
    sourceId: string
    state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    kind: "warning" | "incident"
    hazard: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
    level: "none" | "advice" | "watch_and_act" | "emergency_warning"
    status?: string
    statusClass: "unknown" | "active" | "contained" | "controlled"
    title: string
    category?: string
    location?: string
    lga?: string
    action?: string
    description?: string
    size?: string
    agency?: string
    resources?: number
    updated: number
    published?: number
    url?: string
    point: [number, number]
    polygons?: [number, number][][][]
  }
  ended: boolean
  history: {
    at: number
    change: string
    incident: {
      url?: string
      status?: string
      kind: "warning" | "incident"
      level: "none" | "advice" | "watch_and_act" | "emergency_warning"
      title: string
      description?: string
      location?: string
      id: string
      size?: string
      category?: string
      action?: string
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
      sourceId: string
      published?: number
      resources?: number
      updated: number
      hazard: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
      statusClass: "unknown" | "active" | "contained" | "controlled"
      lga?: string
      agency?: string
      point: [number, number]
    }
  }[]
  feed?: {
    sourceId: string
    label: string
    state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU"
    attribution: string
    licence: string
    licenceUrl?: string
    homepage?: string
    count: number
    ok: boolean
    checkedAt?: number
    changedAt?: number
    error?: string
  }
}

GET /v1/orgs/:orgId/maps/zones/config

Hazards, levels, zone events, states and a new zone's defaults for the caller.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  hazards: {
    id: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
    label: string
    icon: string
    glyph: "car" | "tree" | "info" | "cyclone" | "smoke" | "flame" | "grass" | "burn" | "house" | "drop" | "bolt" | "sun" | "wave" | "quake" | "slope" | "hazard" | "buoy" | "cross" | "plug" | "paw"
    group: string
  }[]
  hazardGroups: {
    id: string
    label: string
  }[]
  levels: {
    id: "none" | "advice" | "watch_and_act" | "emergency_warning"
    label: string
    callToAction: null | string
    token: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground" | "primary" | "destructive" | "muted-foreground" | "background" | "incident" | "map-horizon" | "map-sky" | "map-space" | "incident-foreground" | "warning-advice" | "warning-advice-foreground" | "warning-watch" | "warning-watch-foreground" | "warning-emergency" | "warning-emergency-foreground"
    foreground: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground" | "primary" | "destructive" | "muted-foreground" | "background" | "incident" | "map-horizon" | "map-sky" | "map-space" | "incident-foreground" | "warning-advice" | "warning-advice-foreground" | "warning-watch" | "warning-watch-foreground" | "warning-emergency" | "warning-emergency-foreground"
  }[]
  statuses: {
    id: "unknown" | "active" | "contained" | "controlled"
    label: string
  }[]
  events: {
    type: "new" | "closed" | "updated" | "escalated" | "downgraded"
    label: string
  }[]
  states: {
    id: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    label: string
  }[]
  defaults: {
    channels: ("email" | "sms" | "call" | "mobile" | "web")[]
    smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
    events: {
      new: boolean
      closed: boolean
      updated: boolean
      escalated: boolean
      downgraded: boolean
    }
  }
}

GET /v1/orgs/:orgId/maps/zones

Watch zones: areas where emergency incidents and warnings alert (those the caller can see).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  zones: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }[]
}

POST /v1/orgs/:orgId/maps/zones

Creates a watch zone: areas (circle, polygon, location, near_me or state; up to 10), hazards (empty: all), minLevel, events and notify (channels, people, groups, smsEvents, smsMinLevel, quiet hours with allowLevels). Channels left out get the caller's defaults.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  zone: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/zones/:zoneId

One watch zone, with its status (active, paused or waiting_location for a near-me zone without a current location).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  zone: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }
}

PATCH /v1/orgs/:orgId/maps/zones/:zoneId

Changes a watch zone (its owner, or an org admin for org zones): fields sent replace the stored ones.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  zone: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }
}

Errors

StatusMessage
400Send JSON.

POST /v1/orgs/:orgId/maps/zones/:zoneId/pause

Pauses a watch zone: nothing is checked or sent until it's resumed.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  zone: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }
}

POST /v1/orgs/:orgId/maps/zones/:zoneId/resume

Resumes a paused watch zone.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  zone: {
    name: string
    visibility: "org" | "private"
    areas: {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    } | {
      kind: "location"
      locationId: string
      radiusKm: number
    } | {
      kind: "near_me"
      radiusKm: number
    } | {
      kind: "state"
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
    }[]
    hazards: ("other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal")[]
    minLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
    events: {
      new?: boolean
      closed?: boolean
      updated?: boolean
      escalated?: boolean
      downgraded?: boolean
    }
    notify: {
      smsEvents: null | ("new" | "closed" | "updated" | "escalated" | "downgraded")[]
      smsMinLevel: "none" | "advice" | "watch_and_act" | "emergency_warning"
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      includeOwner: boolean
      groupIds: string[]
      userIds: string[]
      smsFormat: "full" | "short"
      smsPrefix: boolean
      quietHours?: null | {
        start: string
        end: string
        timeZone: string
        allowLevels: ("none" | "advice" | "watch_and_act" | "emergency_warning")[]
      }
      timeZone?: string
    }
    paused: boolean
    zoneId: string
    orgId: string
    ownerId: string
    createdAt: number
    updatedAt: number
    lastEventAt?: number
    alertsSent: number
    personal?: boolean
    status: "active" | "paused" | "waiting_location"
  }
}

POST /v1/orgs/:orgId/maps/zones/:zoneId/test

Sends the caller a sample alert on the zone's channels that reach them; per-channel results.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  mobile?: {
    devices: number
    queued: boolean
  }
  web?: {
    sent: number
    devices: number
  }
  email?: {
    sent?: boolean
    skipped?: string
  }
  sms?: {
    sent?: boolean
    error?: string
    skipped?: string
  }
  call?: {
    skipped: string
  }
}

GET /v1/orgs/:orgId/maps/zones/:zoneId/events

A watch zone's alerts, newest first (limit, cursor): the change, the incident as it was, the distance and what was sent. Kept 30 days (7 for near-me zones).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).
Query parameterTypeRequiredDefaultNotes
limitstringNo50
cursorstringNo

Response 200

{
  events: {
    eventId: string
    zoneId: string
    type: "new" | "closed" | "updated" | "escalated" | "downgraded"
    at: number
    incidentId: string
    title: string
    text: string
    from?: "none" | "advice" | "watch_and_act" | "emergency_warning"
    incident: {
      url?: string
      status?: string
      kind: "warning" | "incident"
      level: "none" | "advice" | "watch_and_act" | "emergency_warning"
      title: string
      description?: string
      location?: string
      id: string
      size?: string
      category?: string
      action?: string
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
      sourceId: string
      published?: number
      resources?: number
      updated: number
      hazard: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
      statusClass: "unknown" | "active" | "contained" | "controlled"
      lga?: string
      agency?: string
      point: [number, number]
    }
    distanceKm?: number
    delivery?: {
      status: "failed" | "sent" | "duplicate" | "quiet" | "no_recipients"
      channels?: string[]
      recipients?: number
      errors?: string[]
      duplicates?: {
        recipients: number
      }
    }
  }[]
  cursor: null | string
}

DELETE /v1/orgs/:orgId/maps/zones/:zoneId

Deletes a watch zone. Its alerts expire on their own.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:zoneIdWatch zone id (izn_…).

Response 200

{
  ok: true
}

GET /v1/orgs/:orgId/maps/zone-events

Recent zone alerts across the zones the caller can see.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
Query parameterTypeRequiredDefaultNotes
limitstringNo50

Response 200

{
  events: {
    eventId: string
    zoneId: string
    type: "new" | "closed" | "updated" | "escalated" | "downgraded"
    at: number
    incidentId: string
    title: string
    text: string
    from?: "none" | "advice" | "watch_and_act" | "emergency_warning"
    incident: {
      url?: string
      status?: string
      kind: "warning" | "incident"
      level: "none" | "advice" | "watch_and_act" | "emergency_warning"
      title: string
      description?: string
      location?: string
      id: string
      size?: string
      category?: string
      action?: string
      state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA"
      sourceId: string
      published?: number
      resources?: number
      updated: number
      hazard: "other" | "bushfire" | "grass_fire" | "burn_off" | "structure_fire" | "vehicle_fire" | "fire" | "flood" | "storm" | "cyclone" | "tree_down" | "heat" | "tsunami" | "earthquake" | "landslide" | "hazmat" | "smoke" | "rescue" | "medical" | "accident" | "power" | "animal"
      statusClass: "unknown" | "active" | "contained" | "controlled"
      lga?: string
      agency?: string
      point: [number, number]
    }
    distanceKm?: number
    delivery?: {
      status: "failed" | "sent" | "duplicate" | "quiet" | "no_recipients"
      channels?: string[]
      recipients?: number
      errors?: string[]
      duplicates?: {
        recipients: number
      }
    }
  }[]
}

GET /v1/orgs/:orgId/maps/locations

The org's alert locations: locationId, name, lat, lon, siteRadiusM (within it counts as on site), address, note, icon, color, ownerId.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  locations: {
    locationId: string
    orgId: string
    name: string
    lat: number
    lon: number
    siteRadiusM: number
    address?: string
    note?: string
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    ownerId: string
    createdAt: number
    updatedAt: number
  }[]
}

POST /v1/orgs/:orgId/maps/locations

Creates an alert location: name, lat, lon, siteRadiusM (50–5,000 m, default 300), optional address, note, icon, color. Up to 50 per organization.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  location: {
    locationId: string
    orgId: string
    name: string
    lat: number
    lon: number
    siteRadiusM: number
    address?: string
    note?: string
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/locations/:locationId

One alert location.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:locationIdLocation id: a platform location, or under Maps an org's alert location (mlc_…).

Response 200

{
  location: {
    locationId: string
    orgId: string
    name: string
    lat: number
    lon: number
    siteRadiusM: number
    address?: string
    note?: string
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

PATCH /v1/orgs/:orgId/maps/locations/:locationId

Changes an alert location; fields sent replace the stored ones. Moving it moves every watch that uses it. Its owner or an org admin.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:locationIdLocation id: a platform location, or under Maps an org's alert location (mlc_…).

Response 200

{
  location: {
    locationId: string
    orgId: string
    name: string
    lat: number
    lon: number
    siteRadiusM: number
    address?: string
    note?: string
    icon: "map-pin" | "star" | "heart" | "home" | "briefcase" | "utensils" | "coffee" | "plane" | "helicopter" | "flag" | "camera" | "car" | "tent" | "hospital" | "shield"
    color: "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "foreground"
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

DELETE /v1/orgs/:orgId/maps/locations/:locationId

Deletes an alert location. 409, naming them, while watches use it. Its owner or an org admin.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:locationIdLocation id: a platform location, or under Maps an org's alert location (mlc_…).

Response 200

{
  ok: true
}

GET /v1/orgs/:orgId/maps/locations/:locationId/watches

Watches that alert near a location (watchId, name), those the caller can see.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:locationIdLocation id: a platform location, or under Maps an org's alert location (mlc_…).

Response 200

{
  watches: {
    watchId: string
    name: string
  }[]
}

GET /v1/orgs/:orgId/maps/groups

Notification groups with counts of active, pending and unsubscribed recipients.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  groups: {
    counts: {
      active: number
      pending: number
      unsubscribed: number
    }
    canEdit: boolean
    groupId: string
    orgId: string
    name: string
    ownerId: string
    createdAt: number
    updatedAt: number
  }[]
}

POST /v1/orgs/:orgId/maps/groups

Creates a notification group (name).

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  group: {
    groupId: string
    orgId: string
    name: string
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

GET /v1/orgs/:orgId/maps/groups/:groupId

A group and, for its owner and org admins, its recipients.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).

Response 200

{
  group: {
    members: {
      memberId: string
      groupId: string
      kind: "user" | "contact"
      userId?: string
      name?: string
      email?: string
      phone?: string
      channels: ("email" | "sms" | "call" | "mobile" | "web")[]
      status: "active" | "pending" | "unsubscribed"
      createdAt: number
      confirmedAt?: number
      unsubscribedAt?: number
    }[]
    canEdit: boolean
    groupId: string
    orgId: string
    name: string
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

PATCH /v1/orgs/:orgId/maps/groups/:groupId

Renames a group.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).

Response 200

{
  group: {
    groupId: string
    orgId: string
    name: string
    ownerId: string
    createdAt: number
    updatedAt: number
  }
}

Errors

StatusMessage
400Send JSON.

DELETE /v1/orgs/:orgId/maps/groups/:groupId

Deletes a group and its recipients. Watches that used it stop sending to them.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).

Response 200

{
  ok: true
}

POST /v1/orgs/:orgId/maps/groups/:groupId/members

Adds a recipient: an organization member ({ kind: "user", userId }, active at once) or anyone ({ kind: "contact", name?, email?, phone? } with phone in E.164), who is asked to confirm first and stays pending until they do. channels limits what they get.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).

Response 201

{
  member: {
    memberId: string
    groupId: string
    kind: "user" | "contact"
    userId?: string
    name?: string
    email?: string
    phone?: string
    channels: ("email" | "sms" | "call" | "mobile" | "web")[]
    status: "active" | "pending" | "unsubscribed"
    createdAt: number
    confirmedAt?: number
    unsubscribedAt?: number
  }
  invited: boolean
}

Errors

StatusMessage
400Send JSON.

PATCH /v1/orgs/:orgId/maps/groups/:groupId/members/:memberId

Changes a recipient's name or channels.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).
:memberIdRecipient id (nmb_…).

Response 200

{
  member: {
    memberId: string
    groupId: string
    kind: "user" | "contact"
    userId?: string
    name?: string
    email?: string
    phone?: string
    channels: ("email" | "sms" | "call" | "mobile" | "web")[]
    status: "active" | "pending" | "unsubscribed"
    createdAt: number
    confirmedAt?: number
    unsubscribedAt?: number
  }
}

Errors

StatusMessage
400Send JSON.

POST /v1/orgs/:orgId/maps/groups/:groupId/members/:memberId/resend

Asks a pending recipient to confirm again (at most three times, an hour apart).

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).
:memberIdRecipient id (nmb_…).

Response 200

{
  invited: boolean
}

DELETE /v1/orgs/:orgId/maps/groups/:groupId/members/:memberId

Removes a recipient.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).
:groupIdNotification group id (ngr_…).
:memberIdRecipient id (nmb_…).

Response 200

{
  ok: true
}

GET /v1/orgs/:orgId/maps/push

Where the caller's alerts can go: the web push public key (VAPID), browsers with web push (subscriptions) and phones that get Maps alerts (phones: deviceId, appId maps, or home on phones without the Maps app, name, model, platform, appVersion, updatedAt).

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  publicKey: string
  subscriptions: {
    subId: string
    label?: string
    host: string
    createdAt: number
    lastUsedAt?: number
  }[]
  phones: {
    deviceId: string
    appId: "maps" | "home"
    name?: string
    model?: string
    platform: string
    appVersion?: string
    updatedAt?: number
  }[]
}

POST /v1/orgs/:orgId/maps/push/test

Sends the caller a sample alert on their phones (Maps app) and browsers: mobile (devices, queued) and web (sent, devices).

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 200

{
  mobile?: {
    devices: number
    queued: boolean
  }
  web?: {
    sent: number
    devices: number
  }
  email?: {
    sent?: boolean
    skipped?: string
  }
  sms?: {
    sent?: boolean
    error?: string
    skipped?: string
  }
  call?: {
    skipped: string
  }
}

POST /v1/orgs/:orgId/maps/push/subscriptions

Subscribes a device to web push: the browser's PushSubscription (endpoint, keys.p256dh, keys.auth) and an optional label. People only; up to 20 devices.

Auth: user access token or platform agent key · Scopes: maps:read, maps:write

Path parameterDescription
:orgIdOrganization id (org_…).

Response 201

{
  subId: string
}

Errors

StatusMessage
400Send JSON.

DELETE /v1/orgs/:orgId/maps/push/subscriptions/:subId

Removes one of the caller's devices.

Auth: user access token or platform agent key · Scope: maps:read

Path parameterDescription
:orgIdOrganization id (org_…).
:subIdPush subscription id, as listed by GET /v1/orgs/:orgId/maps/push.

Response 200

{
  ok: true
}

GET /v1/me/maps/location

Your location for near-me alerts: sharing, the phones sharing (devices: deviceId, platform, name, mode foreground or background, retentionHours, enabledAt, lastReportAt), the current fix (current: lat, lon to 3 decimals, accuracyM, at, deviceName, validUntil, or null) and the near-me watches using it (usedBy). From the Maps or mobile apps only.

Auth: user access token or platform agent key

Response 200

{
  sharing: boolean
  devices: {
    lastReportAt?: number
    mode: "foreground" | "background"
    retentionHours: number
    enabledAt: number
    name?: string
    deviceId: string
    platform: string
  }[]
  current: null | {
    deviceId: string
    mode: string
    validUntil: number
    deviceName?: string
    lat: number
    lon: number
    accuracyM: number
    at: number
  }
  usedBy: {
    orgId: string
    watchId: string
    name: string
  }[]
}

Errors

StatusMessage
403Your location is yours: open it in the Maps app.

PUT /v1/me/maps/location/devices/:deviceId

Turns sharing on for this phone or changes it: platform, name, mode (foreground: counts 30 minutes after each report; background: retentionHours 1, 6 or 24 after the last report). A background phone gets its report key (key, si_loc_…) once. Mobile app tokens only, tied to their sign-in.

Auth: user access token or platform agent key

Path parameterDescription
:deviceIdDevice id: an agent's (dev_…); under /v1/me/notifications a phone's install id; under /v1/me/sign-in-approvals a phone that approves sign-ins (apd_…); under /v1/me/vault the id a device made for its vault (22 base64url characters).

Response 200

{
  key?: string
  device: {
    lastReportAt?: number
    mode: "foreground" | "background"
    retentionHours: number
    enabledAt: number
    name?: string
    deviceId: string
    platform: string
  }
}

Errors

StatusMessage
400Send JSON.
403Your location is yours: open it in the Maps app.
403Phones share their location from the Maps app.

POST /v1/me/maps/location/devices/:deviceId/report

Reports this phone's location: lat, lon, accuracyM, at (ms; at most 10 minutes old), source (foreground, significant, region, open). Stored to 3 decimals, accuracy at least 50 m, one current fix per phone; 204 when dropped (one report per 20 s). Mobile app tokens only.

Auth: user access token or platform agent key

Path parameterDescription
:deviceIdDevice id: an agent's (dev_…); under /v1/me/notifications a phone's install id; under /v1/me/sign-in-approvals a phone that approves sign-ins (apd_…); under /v1/me/vault the id a device made for its vault (22 base64url characters).

Response 200

{
  ok: true
  validUntil?: number
}

Response 204 with no body.

Errors

StatusMessage
400Send JSON.
403Your location is yours: open it in the Maps app.
403Phones share their location from the Maps app.

DELETE /v1/me/maps/location/devices/:deviceId

Stops sharing on one phone: its fix and key are deleted.

Auth: user access token or platform agent key

Path parameterDescription
:deviceIdDevice id: an agent's (dev_…); under /v1/me/notifications a phone's install id; under /v1/me/sign-in-approvals a phone that approves sign-ins (apd_…); under /v1/me/vault the id a device made for its vault (22 base64url characters).

Response 200

{
  ok: boolean
}

Errors

StatusMessage
403Your location is yours: open it in the Maps app.

DELETE /v1/me/maps/location

Deletes your location everywhere: every phone's fix, the phones and their keys. Near-me watches wait until a phone shares again.

Auth: user access token or platform agent key

Response 200

{
  devices: number
  ok: true
}

Errors

StatusMessage
403Your location is yours: open it in the Maps app.

GET /v1/me/maps/location/activity

Your own log of turning sharing on, background on, stopping, deleting and phones removed at sign-out (action, at, deviceName), kept 90 days. Never coordinates.

Auth: user access token or platform agent key

Query parameterTypeRequiredDefaultNotes
limitstringNo50

Response 200

{
  activity: {
    deviceName?: string
    action: string
    at: number
  }[]
}

Errors

StatusMessage
403Your location is yours: open it in the Maps app.