GraphQL API
The Apollon API provides a full GraphQL interface with query/mutation support via async-graphql.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/v1/gql | POST | GraphQL query/mutation endpoint |
/v1/graphiql | GET | GraphiQL IDE (disabled when docs.graphiql = false) |
/v1/schema | GET | GraphQL schema in SDL format (plain text) |
Custom Scalars
| Scalar | Format | Example |
|---|---|---|
DateTime | RFC 3339 string | "2026-06-09T12:00:00Z" |
UUID | Standard UUID string | "550e8400-e29b-41d4-a716-446655440000" |
Queries
gps
Current GPS position from the Peplink router. Returns null if the GPS provider is unreachable.
query {
gps {
id
timestamp
latitude
longitude
altitude
speedKmh
speedMps
speedMph
speedKnots
tripId
}
}
gpsSpeed
Current speed from the Peplink router. Returns null if unreachable.
query {
gpsSpeed {
speedKmh
}
}
gpsHistory
Historical GPS records in a time range. Both parameters are unix timestamps (seconds). Defaults to the last 24 hours.
query {
gpsHistory(start: 1717848000, end: 1717934400) {
start
end
count
records {
id
timestamp
latitude
longitude
altitude
speedKmh
tripId
}
}
}
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
start | Int | No | 24h ago | Start of time range (unix timestamp) |
end | Int | No | now | End of time range (unix timestamp) |
trips
All trips, ordered by start time descending.
query {
trips {
id
startTime
endTime
cargo
weight
status
logGps
category {
id
name
}
sender {
id
name
}
receiver {
id
name
}
gpsLocations {
latitude
longitude
speedKmh
timestamp
}
}
}
trip
A single trip by ID.
query {
trip(id: "550e8400-e29b-41d4-a716-446655440000") {
id
cargo
status
startLatitude
startLongitude
endLatitude
endLongitude
category { name }
sender { name }
receiver { name }
gpsLocations { latitude longitude timestamp }
}
}
clients
All clients, ordered by name ascending.
query {
clients {
id
name
email
phone
city
country
contacts {
id
name
email
position
isPrimary
}
tripsAsSender { id cargo status }
tripsAsReceiver { id cargo status }
}
}
client
A single client by ID.
query {
client(id: "550e8400-e29b-41d4-a716-446655440000") {
id
name
email
street
houseNumber
postalCode
city
country
additionalInfo
contacts { id name isPrimary }
tripsAsSender { id cargo }
tripsAsReceiver { id cargo }
}
}
categories
All cargo categories, ordered by name ascending.
query {
categories {
id
name
description
createdAt
updatedAt
trips { id cargo status }
}
}
category
A single cargo category by ID.
query {
category(id: "550e8400-e29b-41d4-a716-446655440000") {
id
name
description
createdAt
updatedAt
trips { id cargo status weight }
}
}
watchlistEntries
All watchlist entries, ordered by name ascending.
query {
watchlistEntries {
id
mmsi
name
categoryId
alertSound
dangerSound
starColor
rangeThresholds
createdAt
updatedAt
}
}
watchlistEntry
A single watchlist entry by ID.
query {
watchlistEntry(id: "550e8400-e29b-41d4-a716-446655440000") {
id
mmsi
name
categoryId
starColor
rangeThresholds
}
}
watchlistCategories
All watchlist categories, ordered by name ascending.
query {
watchlistCategories {
id
name
color
displayType
soundEnabled
overlayEnabled
overlayIcon
alertSound
dangerSound
rangeThresholds
createdAt
updatedAt
}
}
watchlistCategory
A single watchlist category by ID.
query {
watchlistCategory(id: "550e8400-e29b-41d4-a716-446655440000") {
id
name
color
displayType
overlayIcon
}
}
watchlistSettings
Global watchlist settings (singleton). Returns null if not yet initialized.
query {
watchlistSettings {
id
rangeThresholds
defaultAlertSound
defaultDangerSound
defaultStarColor
updatedAt
}
}
watchlistSounds
All available watchlist sounds (built-in + custom).
query {
watchlistSounds {
id
key
name
volume
fileId
builtin
createdAt
}
}
health
System health check covering database, Peplink, forwarding, and WebSocket status.
query {
health {
status
uptime
database
peplink
forwarding
websocket
websocketClients
timestamp
}
}
pegel
Nearest water gauge station from Pegelonline WSV. Returns null if disabled or no station found.
With stations whitelist configured: returns the nearest whitelisted station (no radius limit).
Without whitelist: searches by GPS proximity within radius / server config.
query($water: String, $radius: Float) {
pegel(water: $water, radius: $radius) {
stationName
waterName
km
latitude
longitude
value
unit
timestamp
state
}
}
| Argument | Type | Description |
|---|---|---|
water | String | Filter by water body name (e.g. RHEIN) |
radius | Float | Search radius in km (whitelist mode: ignored, radius mode: capped at server config) |
activeVessels
AIS vessels seen within the look-back window, ordered by lastSeen descending (newest first). Records come from the persistent vessels registry, so this includes vessels no longer on the live map.
query {
activeVessels(withinSecs: 3600) {
id
mmsi
name
shipType
length
beam
callsign
firstSeen
lastSeen
updatedAt
source
}
}
`source` is the last effective tracking source (`"local"` | `"aisstream"`, null until the first activity flush) — the same value carried live on the `vessels` WS payload. Local has position priority; aisstream takes over on handoff.
| Argument | Type | Required | Description |
|---|---|---|---|
withinSecs | Int! | Yes | Look-back window in seconds — only vessels whose lastSeen falls within it are returned |
vessels
All vessels in the persistent vessels registry, ordered by lastSeen descending (newest first).
query {
vessels {
id
mmsi
name
shipType
length
beam
callsign
firstSeen
lastSeen
updatedAt
}
}
vessel
A single registry vessel by MMSI. Returns null if the MMSI is not in the registry.
query {
vessel(mmsi: 211234567) {
id
mmsi
name
shipType
callsign
lastSeen
}
}
| Argument | Type | Required | Description |
|---|---|---|---|
mmsi | Int! | Yes | Maritime Mobile Service Identity |
vesselHistory
Static-field change audit log from vessels_history, ordered by changedAt descending (newest first). Each entry records one static field change (name, shipType, length, beam, or callsign).
query {
vesselHistory(mmsi: 211234567, limit: 50) {
id
mmsi
field
oldValue
newValue
changedAt
}
}
| Argument | Type | Required | Description |
|---|---|---|---|
mmsi | Int | No | Filter to a single vessel; omit for all vessels |
limit | Int | No | Max entries to return (default 200, max 1000) |
Mutations
createTrip
Create a new trip.
mutation {
createTrip(input: {
startLatitude: 51.5074
startLongitude: -0.1278
cargo: "Electronics"
categoryId: "..."
weight: 250.5
senderId: "..."
receiverId: "..."
logGps: true
}) {
id
startTime
cargo
status
}
}
Input: CreateTripInput
| Field | Type | Required | Default |
|---|---|---|---|
startLatitude | Float! | Yes | -- |
startLongitude | Float! | Yes | -- |
cargo | String! | Yes | -- |
categoryId | UUID! | Yes | -- |
weight | Float! | Yes | -- |
senderId | UUID! | Yes | -- |
receiverId | UUID! | Yes | -- |
logGps | Boolean! | No | false |
updateTrip
Update an existing trip. Only provided fields are changed.
mutation {
updateTrip(input: {
id: "550e8400-..."
cargo: "Updated Electronics"
weight: 300.0
status: CANCELLED
}) {
id
cargo
weight
status
}
}
Input: UpdateTripInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
cargo | String | No |
categoryId | UUID | No |
weight | Float | No |
senderId | UUID | No |
receiverId | UUID | No |
status | GqlTripStatus | No |
logGps | Boolean | No |
deleteTrip
Delete a trip and its associated GPS locations. Returns true on success.
mutation {
deleteTrip(id: "550e8400-...")
}
completeTrip
Complete an active trip by setting end coordinates and status.
mutation {
completeTrip(input: {
id: "550e8400-..."
endLatitude: 52.5200
endLongitude: 13.4050
}) {
id
status
endTime
endLatitude
endLongitude
}
}
Input: CompleteTripInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
endLatitude | Float! | Yes |
endLongitude | Float! | Yes |
createClient
Create a new client, optionally with contact persons.
mutation {
createClient(input: {
name: "ACME Logistics"
email: "info@acme.example.com"
phone: "+49 30 12345678"
street: "Hauptstrasse"
houseNumber: "42"
postalCode: "10115"
city: "Berlin"
country: "DE"
contacts: [{
name: "John Doe"
email: "john@acme.example.com"
phone: "+49 30 12345679"
position: "Logistics Manager"
isPrimary: true
}]
}) {
id
name
contacts { id name isPrimary }
}
}
Input: CreateClientInput
| Field | Type | Required | Default |
|---|---|---|---|
name | String! | Yes | -- |
email | String! | Yes | -- |
phone | String! | Yes | -- |
street | String! | Yes | -- |
houseNumber | String! | Yes | -- |
postalCode | String! | Yes | -- |
city | String! | Yes | -- |
country | String! | Yes | -- |
additionalInfo | String | No | null |
contacts | [CreateContactInput!]! | No | [] |
updateClient
Update an existing client. Only provided fields are changed.
mutation {
updateClient(input: {
id: "550e8400-..."
name: "ACME Logistics GmbH"
city: "Hamburg"
}) {
id
name
city
}
}
Input: UpdateClientInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
name | String | No |
email | String | No |
phone | String | No |
street | String | No |
houseNumber | String | No |
postalCode | String | No |
city | String | No |
country | String | No |
additionalInfo | String | No |
createContact
Add a contact person to an existing client.
mutation {
createContact(
clientId: "550e8400-..."
input: {
name: "Jane Smith"
email: "jane@acme.example.com"
phone: "+49 30 12345680"
position: "Fleet Manager"
isPrimary: false
}
) {
id
name
isPrimary
clientId
}
}
Input: CreateContactInput
| Field | Type | Required | Default |
|---|---|---|---|
name | String! | Yes | -- |
email | String! | Yes | -- |
phone | String! | Yes | -- |
position | String! | Yes | -- |
isPrimary | Boolean! | No | false |
updateContact
Update a contact person. Only provided fields are changed.
mutation {
updateContact(input: {
id: "660e8400-..."
clientId: "550e8400-..."
name: "Jane Doe"
isPrimary: true
}) {
id
name
isPrimary
}
}
Input: UpdateContactInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
clientId | UUID! | Yes |
name | String | No |
email | String | No |
phone | String | No |
position | String | No |
isPrimary | Boolean | No |
deleteContact
Delete a contact person. Returns true on success.
mutation {
deleteContact(contactId: "660e8400-...", clientId: "550e8400-...")
}
createCategory
Create a new cargo category.
mutation {
createCategory(input: {
name: "Fragile Goods"
description: "Items requiring careful handling"
}) {
id
name
description
createdAt
}
}
Input: CreateCategoryInput
| Field | Type | Required |
|---|---|---|
name | String! | Yes |
description | String | No |
updateCategory
Update an existing cargo category. Only provided fields are changed.
mutation {
updateCategory(input: {
id: "550e8400-..."
name: "Fragile Items"
}) {
id
name
updatedAt
}
}
Input: UpdateCategoryInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
name | String | No |
description | String | No |
deleteCategory
Delete a cargo category. Returns false if trips still reference it.
mutation {
deleteCategory(id: "550e8400-...")
}
createWatchlistEntry
Create a new watchlist entry.
mutation {
createWatchlistEntry(input: {
mmsi: "211234567"
name: "MS Harmonie"
categoryId: "..."
alertSound: "sonar"
dangerSound: "alarm"
starColor: "#EF4444"
rangeThresholds: [10.0, 7.0, 5.0, 2.0, 1.0]
}) {
id
mmsi
name
createdAt
}
}
Input: CreateWatchlistEntryInput
| Field | Type | Required | Default |
|---|---|---|---|
mmsi | String! | Yes | -- |
name | String! | Yes | -- |
categoryId | UUID | No | null |
alertSound | String | No | null |
dangerSound | String | No | null |
starColor | String | No | null |
rangeThresholds | JSON | No | null |
updateWatchlistEntry
Update an existing watchlist entry. Only provided fields are changed.
mutation {
updateWatchlistEntry(input: {
id: "550e8400-..."
name: "MS Harmonie II"
starColor: "#F59E0B"
}) {
id
name
starColor
updatedAt
}
}
Input: UpdateWatchlistEntryInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
mmsi | String | No |
name | String | No |
categoryId | UUID | No |
alertSound | String | No |
dangerSound | String | No |
starColor | String | No |
rangeThresholds | JSON | No |
deleteWatchlistEntry
Delete a watchlist entry by ID. Returns true on success.
mutation {
deleteWatchlistEntry(id: "550e8400-...")
}
createWatchlistCategory
Create a new watchlist category.
mutation {
createWatchlistCategory(input: {
name: "Freighter"
color: "#EF4444"
displayType: "stars"
soundEnabled: true
overlayEnabled: true
}) {
id
name
color
createdAt
}
}
Input: CreateWatchlistCategoryInput
| Field | Type | Required | Default |
|---|---|---|---|
name | String! | Yes | -- |
color | String! | Yes | -- |
displayType | String | No | "stars" |
soundEnabled | Boolean | No | true |
overlayEnabled | Boolean | No | true |
overlayIcon | String | No | null |
alertSound | String | No | null |
dangerSound | String | No | null |
rangeThresholds | JSON | No | null |
updateWatchlistCategory
Update an existing watchlist category. Only provided fields are changed.
mutation {
updateWatchlistCategory(input: {
id: "550e8400-..."
name: "Tanker"
color: "#F97316"
}) {
id
name
color
updatedAt
}
}
Input: UpdateWatchlistCategoryInput
| Field | Type | Required |
|---|---|---|
id | UUID! | Yes |
name | String | No |
color | String | No |
displayType | String | No |
soundEnabled | Boolean | No |
overlayEnabled | Boolean | No |
overlayIcon | String | No |
alertSound | String | No |
dangerSound | String | No |
rangeThresholds | JSON | No |
deleteWatchlistCategory
Delete a watchlist category by ID. Returns true on success.
mutation {
deleteWatchlistCategory(id: "550e8400-...")
}
updateWatchlistSettings
Update global watchlist settings (upserts). Only provided fields are changed.
mutation {
updateWatchlistSettings(input: {
rangeThresholds: [8.0, 6.0, 4.0, 2.0, 0.5]
defaultStarColor: "#F59E0B"
}) {
id
rangeThresholds
defaultStarColor
updatedAt
}
}
Input: UpdateWatchlistSettingsInput
| Field | Type | Required |
|---|---|---|
rangeThresholds | JSON | No |
defaultAlertSound | String | No |
defaultDangerSound | String | No |
defaultStarColor | String | No |
deleteWatchlistSound
Delete a custom watchlist sound by ID. Built-in sounds cannot be deleted. Returns error if the sound is still referenced.
mutation {
deleteWatchlistSound(id: "550e8400-...")
}
updateWatchlistSoundVolume
Update the volume of a watchlist sound (0-100).
mutation {
updateWatchlistSoundVolume(id: "550e8400-...", volume: 80) {
id
key
name
volume
}
}
updateVessel
Manually overwrite a vessel's static fields. Full-representation update — the input always carries all 5 editable fields; an omitted field is written as null (no partial merge).
mutation {
updateVessel(input: {
mmsi: 211234567
name: "MS Harmonie"
shipType: 70
length: 110
beam: 11
callsign: "DA1234"
}) {
id
mmsi
name
shipType
length
beam
callsign
}
}
Input: UpdateVesselInput
| Field | Type | Required |
|---|---|---|
mmsi | Int! | Yes |
name | String | No |
shipType | Int | No |
length | Int | No |
beam | Int | No |
callsign | String | No |
deleteVessel
Hard-delete a vessel from the registry. ignore: true also adds the MMSI to the persistent ignore list (union'd with [ais] ignore_mmsi config). Returns true on success.
mutation {
deleteVessel(mmsi: 211234567, ignore: false)
}
| Argument | Type | Required | Description |
|---|---|---|---|
mmsi | Int! | Yes | Maritime Mobile Service Identity |
ignore | Boolean! | Yes | Also blacklist the MMSI persistently |
Output Types
GqlGpsLocation
| Field | Type | Description |
|---|---|---|
id | UUID! | Location record ID |
timestamp | DateTime! | When the location was recorded |
latitude | Float! | Latitude in degrees |
longitude | Float! | Longitude in degrees |
altitude | Float | Altitude in meters (nullable) |
speedKmh | Float! | Speed in km/h |
speedMps | Float! | Speed in m/s |
speedMph | Float! | Speed in mph |
speedKnots | Float! | Speed in knots |
tripId | UUID | Associated trip ID (nullable) |
GqlTrip
| Field | Type | Description |
|---|---|---|
id | UUID! | Trip ID |
startTime | DateTime! | Trip start time |
endTime | DateTime | Trip end time (null if active) |
startLatitude | Float! | Start latitude |
startLongitude | Float! | Start longitude |
endLatitude | Float | End latitude (null if active) |
endLongitude | Float | End longitude (null if active) |
cargo | String! | Cargo description |
weight | Float! | Cargo weight |
status | GqlTripStatus! | ACTIVE, COMPLETED, or CANCELLED |
logGps | Boolean! | Whether GPS logging is enabled |
category | GqlCargoCategory! | Resolved category relation |
sender | GqlClient! | Resolved sender client relation |
receiver | GqlClient! | Resolved receiver client relation |
gpsLocations | [GqlGpsLocation!]! | GPS locations for this trip |
GqlClient
| Field | Type | Description |
|---|---|---|
id | UUID! | Client ID |
name | String! | Client name |
email | String! | Email address |
phone | String! | Phone number |
street | String! | Street name |
houseNumber | String! | House number |
postalCode | String! | Postal code |
city | String! | City |
country | String! | Country |
additionalInfo | String | Additional info (nullable) |
contacts | [GqlContactPerson!]! | Contact persons |
tripsAsSender | [GqlTrip!]! | Trips where client is sender |
tripsAsReceiver | [GqlTrip!]! | Trips where client is receiver |
GqlContactPerson
| Field | Type | Description |
|---|---|---|
id | UUID! | Contact ID |
name | String! | Contact name |
email | String! | |
phone | String! | Phone number |
position | String! | Job position/title |
isPrimary | Boolean! | Whether this is the primary contact |
clientId | UUID! | Parent client ID |
GqlCargoCategory
| Field | Type | Description |
|---|---|---|
id | UUID! | Category ID |
name | String! | Category name |
description | String | Description (nullable) |
createdAt | DateTime! | Creation timestamp |
updatedAt | DateTime! | Last update timestamp |
trips | [GqlTrip!]! | Trips in this category |
GqlTripStatus (Enum)
| Value | Description |
|---|---|
ACTIVE | Trip is in progress |
COMPLETED | Trip has been completed |
CANCELLED | Trip has been cancelled |
GqlWatchlistEntry
| Field | Type | Description |
|---|---|---|
id | UUID! | Entry ID |
mmsi | String! | MMSI number |
name | String! | Display name |
categoryId | UUID | Category ID (nullable) |
alertSound | String | Alert sound key (nullable) |
dangerSound | String | Danger sound key (nullable) |
starColor | String | Hex star color (nullable, inherits from category/settings) |
rangeThresholds | JSON | Distance thresholds array (nullable) |
createdAt | DateTime! | Creation timestamp |
updatedAt | DateTime! | Last update timestamp |
GqlWatchlistCategory
| Field | Type | Description |
|---|---|---|
id | UUID! | Category ID |
name | String! | Category name |
color | String! | Hex color |
displayType | String! | Display mode (stars or custom) |
soundEnabled | Boolean! | Whether sound alerts are enabled |
overlayEnabled | Boolean! | Whether overlay display is enabled |
overlayIcon | String | Lucide icon name (nullable, for custom display) |
alertSound | String | Alert sound key (nullable) |
dangerSound | String | Danger sound key (nullable) |
rangeThresholds | JSON | Distance thresholds array (nullable) |
createdAt | DateTime! | Creation timestamp |
updatedAt | DateTime! | Last update timestamp |
GqlWatchlistSettings
| Field | Type | Description |
|---|---|---|
id | UUID! | Settings ID |
rangeThresholds | JSON! | Default 5-element distance thresholds in km |
defaultAlertSound | String | Default alert sound key (nullable) |
defaultDangerSound | String | Default danger sound key (nullable) |
defaultStarColor | String! | Default hex star color |
updatedAt | DateTime! | Last update timestamp |
GqlWatchlistSound
| Field | Type | Description |
|---|---|---|
id | UUID! | Sound ID |
key | String! | Unique sound key |
name | String! | Display name |
fileId | UUID! | Associated storage file ID |
volume | Int! | Volume level (0-100) |
builtin | Boolean! | Whether this is a built-in sound |
createdAt | DateTime! | Creation timestamp |
VesselRecordGql
| Field | Type | Description |
|---|---|---|
id | UUID! | Vessel registry record ID |
mmsi | Int! | Maritime Mobile Service Identity |
name | String | Vessel name (nullable) |
shipType | Int | Raw AIS ship type code (nullable) |
length | Int | Length in meters (nullable) |
beam | Int | Beam in meters (nullable) |
callsign | String | Radio call sign (nullable) |
firstSeen | DateTime! | First AIS contact |
lastSeen | DateTime! | Most recent AIS contact |
updatedAt | DateTime! | Last registry write |
VesselHistoryGql
| Field | Type | Description |
|---|---|---|
id | UUID! | History entry ID |
mmsi | Int! | Maritime Mobile Service Identity |
field | String! | Changed static field (name, shipType, length, beam, callsign) |
oldValue | String | Previous value (nullable) |
newValue | String | New value (nullable) |
changedAt | DateTime! | Change timestamp |
HealthStatus
| Field | Type | Description |
|---|---|---|
status | String! | Overall status (ok or degraded) |
uptime | Int! | Server uptime in seconds |
database | String! | Database status |
peplink | String! | Peplink provider status |
forwarding | String! | Forwarding service status |
websocket | String! | WebSocket status (running or closing) |
websocketClients | Int! | Number of connected WebSocket clients |
timestamp | DateTime! | Check timestamp |