CactiveDocs

Search docs

Search every page of the documentation.

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, or a notification recipient's confirm and unsubscribe link.

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/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, or a notification recipient's confirm and unsubscribe link.

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, or a notification recipient's confirm and unsubscribe link.

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, or a notification recipient's confirm and unsubscribe link.

Response 200

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

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

What the app needs to draw: the ADS-B source and its credit line, preset areas, event types and channels.

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"]
  areas: {
    id: string
    label: string
    source: string
  }[]
  events: {
    type: "emergency" | "takeoff" | "landing" | "signal_lost" | "signal_resumed" | "area_enter" | "area_leave" | "orbit" | "road_follow" | "road_switch" | "road_repeat" | "road_leave" | "threshold"
    label: string
  }[]
  channels: {
    push: true
    email: boolean
    sms: boolean
    call: boolean
  }
}

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

{
  at: null
  provider: null
  coverage: []
  aircraft: []
  issues: string[]
} | {
  at: number
  provider: {
    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
    emergency?: string
    category?: string
    alt: null | number
    onGround: boolean
    altGeom?: number
    gs?: number
    track?: number
    vs?: number
    callsign?: string
    registration?: string
    squawk?: string
    icon: "unknown" | "helicopter" | "light" | "heavy" | "jet" | "turboprop" | "glider" | "balloon" | "drone" | "ground"
    operator?: string
    road?: string
    trail?: ([number, number, number, null | number, null | number, null | number])[]
  }[]
  issues?: string[]
}

Errors

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

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
    emergency?: string
    category?: string
    alt: null | number
    onGround: boolean
    altGeom?: number
    gs?: number
    track?: number
    vs?: number
    callsign?: string
    registration?: string
    squawk?: string
    icon: "unknown" | "helicopter" | "light" | "heavy" | "jet" | "turboprop" | "glider" | "balloon" | "drone" | "ground"
    operator?: string
    road?: string
  }[]
}

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

One aircraft from the live snapshot with its recent track; 404 when it hasn't been seen in the last 90 seconds.

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

Errors

StatusMessage
404Aircraft not found
404Not seen in the last 90 seconds

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.

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

Path parameterDescription
:orgIdOrganization id (org_…).

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
  }[]
}

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_…).

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_…).

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_…).

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_…).

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_…).
: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_…).
: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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }[]
}

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

Creates a flight watch: name, targets ({ kind: hex | registration | callsign | type, value }, a trailing * matches prefixes), areas ({ kind: "preset", id: "au-vic" }, circles { center: [lon, lat], radiusKm } or polygons { ring: [[lon, lat], …] }), limitToAreas, events (on/off per event type), settings (signal, orbit, roads, thresholds), notify (channels, includeOwner, groupIds, 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. 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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

Errors

StatusMessage
400Send JSON.

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

A flight watch with every setting, defaults filled in.

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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

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: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    areas: {
      kind: "preset"
      id: string
    } | {
      kind: "circle"
      center: [number, number]
      radiusKm: number
      label?: string
    } | {
      kind: "polygon"
      ring: [number, number][]
      label?: string
    }[]
    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
      threshold?: boolean
    }
    settings: {
      signal: {
        lostAfterSeconds: number
        lowAltitudeFt: number
      }
      orbit: {
        turns: number
        radiusKm: number
        windowMinutes: 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: {
      channels: ("email" | "call" | "push" | "sms")[]
      includeOwner: boolean
      groupIds: string[]
      cooldownMinutes: number
      timeZone?: string
      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" | "threshold")[]
      }
    }
    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"
    status: "active" | "paused" | "waiting" | "off_schedule" | "ended"
  }
}

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: "completed" | "ended" | "in_progress"
    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" | "threshold"
      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: "completed" | "ended" | "in_progress"
    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" | "threshold"
      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" | "threshold"
    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" | "quiet" | "cooldown" | "no_recipients"
      channels?: ("email" | "call" | "push" | "sms")[]
      recipients?: number
      errors?: 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" | "threshold"
    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" | "quiet" | "cooldown" | "no_recipients"
      channels?: ("email" | "call" | "push" | "sms")[]
      recipients?: number
      errors?: 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" | "call" | "push" | "sms")[]
      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" | "call" | "push" | "sms")[]
    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" | "call" | "push" | "sms")[]
    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

The web push public key (VAPID) and the caller's subscribed devices.

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
  }[]
}

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
}