GraphQL API

The TowerOps GraphQL API provides a flexible, strongly-typed interface for querying and managing your network infrastructure. Request exactly the data you need in a single request — no over-fetching, no under-fetching.

The GraphQL API uses the same authentication as the <.link navigate={~p"/docs/api"} class="text-blue-600 hover:text-blue-500 dark:text-blue-400" > REST API and provides equivalent functionality with the added benefit of nested queries and precise field selection.

Official clients

Prefer infrastructure-as-code? The Towerops Ansible collection covers every resource exposed here: codeberg.org/towerops/ansible.

Authentication

Authenticate using a Bearer token in the Authorization header. API tokens can be created in your organization settings.

Authorization: Bearer <%= @sample_token %>

Endpoint

All GraphQL requests are sent as POST to a single endpoint:

POST https://app.towerops.net/api/graphql

Send a JSON body with a query field and optional variables:

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "{ devices { id name ipAddress status } }"}'|) %>

Query: Devices

devices

List all devices. Optionally filter by site or status.

Argument Type Description
siteId ID Filter by site
status String Filter by status (up, down, unknown)
limit Int Max results (default: 100)
<%= raw(~S[{
  devices(siteId: "uuid", status: "up") {
    id
    name
    ipAddress
    status
    lastCheckedAt
    monitoringEnabled
    snmpEnabled
    deviceRole
    site {
      id
      name
      location
    }
  }
}]) %>

device

Get a single device by ID.

<%= raw(~S[{
  device(id: "device-uuid") {
    id
    name
    ipAddress
    status
    description
    snmpVersion
    snmpPort
    checkIntervalSeconds
    site { id name }
  }
}]) %>

Query: Sites

sites

List all sites for the organization.

<%= raw(~S[{
  sites {
    id
    name
    description
    location
    snmpVersion
    snmpCommunity
    insertedAt
  }
}]) %>

site

Get a single site by ID.

<%= raw(~S[{ site(id: "site-uuid") { id name location } }]) %>

Query: Alerts

alerts

List alerts. Filter by status or device.

Argument Type Description
status String active, acknowledged, or resolved
deviceId ID Filter by device
limit Int Max results (default: 100)
<%= raw(~S[{
  alerts(status: "active", limit: 10) {
    id
    alertType
    message
    triggeredAt
    acknowledgedAt
    resolvedAt
    deviceId
    device {
      id
      name
      ipAddress
    }
    acknowledgedByEmail
  }
}]) %>

Query: Agents

<%= raw(~S[{
  agents {
    id
    name
    enabled
    lastSeenAt
    lastIp
    isCloudPoller
    deviceCount
    metadata
  }
}

# Single agent
{ agent(id: "agent-uuid") { id name enabled deviceCount } }]) %>

Query: Organization

<%= raw(~S[{
  organization {
    id
    name
    slug
    subscriptionPlan
    useSites
    snmpVersion
    snmpCommunity
    snmpPort
    snmpTransport
    mikrotikEnabled
    mikrotikUsername
    mikrotikPort
    mikrotikUseSsl
    defaultAgentTokenId
  }
}]) %>

Query: Members

<%= raw(~S[{
  members {
    id
    email
    role
    insertedAt
  }
}]) %>

Query: Integrations

<%= raw(~S[{
  integrations {
    id
    provider
    enabled
    syncIntervalMinutes
    lastSyncedAt
    lastSyncStatus
  }
}]) %>

Query: Activity

<%= raw(~S[{
  activity(limit: 20) {
    summary
    detail
    timestamp
    severity
    deviceName
    siteName
    link
    type
  }
}]) %>

Query: Metrics & Interfaces

deviceMetrics

Get time-series metrics for a device.

Argument Type Description
deviceId ID! Required device ID
sensorType String Filter by sensor type
timeRange String e.g. "24h", "72h" (default: "24h")
<%= raw(~S[{
  deviceMetrics(deviceId: "device-uuid", timeRange: "48h") {
    timestamp
    value
    status
    checkName
    checkType
  }
}]) %>

deviceInterfaces

Get SNMP interfaces for a device.

<%= raw(~S[{
  deviceInterfaces(deviceId: "device-uuid") {
    id
    ifIndex
    ifName
    ifDescr
    ifType
    ifSpeed
    ifAdminStatus
    ifOperStatus
    ifAlias
  }
}]) %>

Query: Time-Series

Query historical sensor readings, interface traffic rates, and monitoring check results. All time-series queries support a timeRange argument (e.g. "1h", "24h", "72h").

deviceSensors

List all SNMP sensors discovered on a device.

Argument Type Description
deviceId ID! Required device ID
<%= raw(~S[{
  deviceSensors(deviceId: "device-uuid") {
    id
    sensorType
    sensorUnit
    sensorDescr
    currentValue
    lastCheckedAt
    monitored
  }
}]) %>

sensorReadings

Get historical readings for a specific sensor.

Argument Type Description
sensorId ID! Required sensor ID
timeRange String e.g. "1h", "24h", "72h" (default: "24h")
<%= raw(~S[{
  sensorReadings(sensorId: "sensor-uuid", timeRange: "24h") {
    sensorId
    value
    status
    checkedAt
  }
}]) %>

interfaceTraffic

Get traffic rates (bits per second) computed from SNMP counter deltas for a network interface.

Argument Type Description
interfaceId ID! Required interface ID
timeRange String e.g. "1h", "24h", "72h" (default: "24h")
<%= raw(~S[{
  interfaceTraffic(interfaceId: "interface-uuid", timeRange: "24h") {
    timestamp
    inBps
    outBps
    inErrors
    outErrors
  }
}]) %>

checkResults

Get historical results for a monitoring check (ping, HTTP, etc.).

Argument Type Description
checkId ID! Required check ID
timeRange String e.g. "1h", "24h" (default: "1h")
<%= raw(~S[{
  checkResults(checkId: "check-uuid", timeRange: "1h") {
    checkId
    value
    status
    checkedAt
    responseTimeMs
  }
}]) %>

Query: Service Checks

List and inspect HTTP, TCP, DNS, and ping service checks.

checks

<%= raw(~S[{
  checks(checkType: "http", deviceId: "device-uuid") {
    id
    name
    checkType
    enabled
    intervalSeconds
    currentState
    config
  }
}]) %>

check (single)

<%= raw(~S[{
  check(id: "check-uuid") {
    id
    name
    checkType
    config
    currentState
    currentStateType
    lastCheckAt
  }
}]) %>

Query: On-Call Schedules

Inspect schedules and find out who is currently on call.

schedules

<%= raw(~S[{
  schedules {
    id
    name
    timezone
    description
  }
}]) %>

schedule (with layers + overrides)

<%= raw(~S[{
  schedule(id: "schedule-uuid") {
    id
    name
    timezone
    layers {
      id
      name
      position
      rotationType
      members {
        userId
        position
      }
    }
    overrides {
      id
      userId
      startTime
      endTime
    }
  }
}]) %>

onCall

<%= raw(~S[{
  onCall(scheduleId: "schedule-uuid") {
    userId
    email
  }
}]) %>

Query: Escalation Policies

Inspect escalation policies and their rules + targets.

escalationPolicies

<%= raw(~S[{
  escalationPolicies {
    id
    name
    description
    repeatCount
    repeatIntervalMinutes
  }
}]) %>

escalationPolicy (with rules + targets)

<%= raw(~S[{
  escalationPolicy(id: "policy-uuid") {
    id
    name
    rules {
      id
      position
      delayMinutes
      targets {
        id
        targetType
        userId
        scheduleId
      }
    }
  }
}]) %>

Query: Maintenance Windows

Inspect planned and active maintenance windows. The optional filter argument accepts active, upcoming, or past.

maintenanceWindows

<%= raw(~S[{
  maintenanceWindows(filter: "upcoming") {
    id
    name
    startsAt
    endsAt
    suppressAlerts
    siteId
    deviceId
  }
}]) %>

Mutation: Devices

createDevice

<%= raw(~S[mutation {
  createDevice(input: {
    ipAddress: "192.168.1.1"
    name: "Core Router"
    siteId: "site-uuid"
    snmpEnabled: true
    snmpVersion: "2c"
    snmpCommunity: "public"
  }) {
    id
    name
    ipAddress
    status
  }
}]) %>

updateDevice

<%= raw(~S[mutation {
  updateDevice(id: "device-uuid", input: {
    name: "Updated Router"
    monitoringEnabled: false
  }) {
    id
    name
    monitoringEnabled
  }
}]) %>

deleteDevice

<%= raw(~S[mutation {
  deleteDevice(id: "device-uuid") {
    success
    message
  }
}]) %>

Mutation: Sites

createSite

<%= raw(~S[mutation {
  createSite(input: {
    name: "Main Office"
    location: "New York, NY"
    snmpCommunity: "public"
  }) {
    id
    name
    location
  }
}]) %>

updateSite / deleteSite

Same pattern as device mutations.

Mutation: Alert Actions

acknowledgeAlert

<%= raw(~S[mutation {
  acknowledgeAlert(id: "alert-uuid") {
    id
    acknowledgedAt
    acknowledgedByEmail
  }
}]) %>

resolveAlert

<%= raw(~S[mutation {
  resolveAlert(id: "alert-uuid") {
    id
    resolvedAt
  }
}]) %>

Mutation: Agent Management

createAgent

Creates a new agent token. The raw token is only returned once.

<%= raw(~S[mutation {
  createAgent(name: "DC1 Agent") {
    id
    name
    token
  }
}]) %>

deleteAgent

<%= raw(~S[mutation { deleteAgent(id: "agent-uuid") { success message } }]) %>

Mutation: Organization Settings

<%= raw(~S[mutation {
  updateOrganization(input: {
    name: "My Network"
    useSites: true
    snmpVersion: "2c"
    snmpCommunity: "public"
    mikrotikEnabled: true
    mikrotikUsername: "admin"
    mikrotikPort: 8729
    mikrotikUseSsl: true
  }) {
    id
    name
    useSites
    snmpVersion
    mikrotikEnabled
  }
}]) %>

Mutation: Members

sendInvitation

<%= raw(~S[mutation {
  sendInvitation(email: "user@example.com", role: "admin") {
    id
    email
    role
    expiresAt
  }
}]) %>

updateMemberRole

<%= raw(~S[mutation {
  updateMemberRole(id: "user-uuid", role: "admin") {
    id
    email
    role
  }
}]) %>

removeMember / cancelInvitation

<%= raw(~S[mutation { removeMember(id: "user-uuid") { success message } }
mutation { cancelInvitation(id: "invitation-uuid") { success message } }]) %>

Mutation: Integrations

createIntegration

<%= raw(~S[mutation {
  createIntegration(input: {
    provider: "preseem"
    enabled: true
    credentials: "{\"api_key\": \"xxx\"}"
    syncIntervalMinutes: 10
  }) {
    id
    provider
    enabled
  }
}]) %>

testIntegration

<%= raw(~S[mutation {
  testIntegration(id: "integration-uuid") {
    success
    message
  }
}]) %>

Mutation: Service Checks

createCheck

<%= raw(~S[mutation {
  createCheck(input: {
    name: "API health"
    checkType: "http"
    deviceId: "device-uuid"
    intervalSeconds: 60
    config: {url: "https://api.example.com/health", method: "GET"}
  }) {
    id
    name
    checkType
  }
}]) %>

updateCheck / deleteCheck

<%= raw(~S[mutation {
  updateCheck(id: "check-uuid", input: {intervalSeconds: 30}) {
    id
    intervalSeconds
  }
}

mutation {
  deleteCheck(id: "check-uuid") {
    success
  }
}]) %>

Mutation: Schedules

Schedule CRUD

<%= raw(~S[mutation {
  createSchedule(input: {
    name: "Primary on-call"
    timezone: "America/Chicago"
  }) {
    id
    name
  }
}

mutation {
  updateSchedule(id: "schedule-uuid", input: {description: "Weekly rotation"}) {
    id
    description
  }
}

mutation {
  deleteSchedule(id: "schedule-uuid") {
    success
  }
}]) %>

Layers + members

<%= raw(~S[mutation {
  createLayer(scheduleId: "schedule-uuid", input: {
    name: "Primary"
    position: 0
    rotationType: "weekly"
    rotationInterval: 1
    handoffTime: "09:00:00"
    startDate: "2026-06-01T00:00:00Z"
  }) {
    id
  }
}

mutation {
  addLayerMember(layerId: "layer-uuid", userId: "user-uuid", position: 0) {
    id
  }
}

mutation {
  removeLayerMember(id: "member-uuid") {
    success
  }
}]) %>

Overrides

<%= raw(~S[mutation {
  createOverride(scheduleId: "schedule-uuid", input: {
    userId: "user-uuid"
    startTime: "2026-06-07T17:00:00Z"
    endTime:   "2026-06-09T09:00:00Z"
  }) {
    id
  }
}

mutation {
  deleteOverride(id: "override-uuid") {
    success
  }
}]) %>

Mutation: Escalation Policies

Policy CRUD

<%= raw(~S[mutation {
  createEscalationPolicy(input: {
    name: "Critical alerts"
    repeatCount: 3
    repeatIntervalMinutes: 15
  }) {
    id
    name
  }
}

mutation {
  updateEscalationPolicy(id: "policy-uuid", input: {name: "Critical alerts (revised)"}) {
    id
  }
}

mutation {
  deleteEscalationPolicy(id: "policy-uuid") {
    success
  }
}]) %>

Rules + targets

<%= raw(~S[mutation {
  createEscalationRule(escalationPolicyId: "policy-uuid", input: {
    position: 0
    delayMinutes: 5
  }) {
    id
  }
}

mutation {
  createEscalationTarget(escalationRuleId: "rule-uuid", input: {
    targetType: "schedule"
    scheduleId: "schedule-uuid"
  }) {
    id
  }
}

mutation {
  deleteEscalationTarget(id: "target-uuid") {
    success
  }
}]) %>

Mutation: Maintenance Windows

CRUD

<%= raw(~S[mutation {
  createMaintenanceWindow(input: {
    name: "Quarterly firmware roll"
    startsAt: "2026-06-01T05:00:00Z"
    endsAt:   "2026-06-01T09:00:00Z"
    siteId: "site-uuid"
    suppressAlerts: true
  }) {
    id
    name
  }
}

mutation {
  updateMaintenanceWindow(id: "window-uuid", input: {endsAt: "2026-06-01T10:00:00Z"}) {
    id
    endsAt
  }
}

mutation {
  deleteMaintenanceWindow(id: "window-uuid") {
    success
  }
}]) %>

Subscriptions

Subscribe to real-time events via WebSocket. Connect to wss://app.towerops.net/socket/graphql with your API token as a connection parameter.

Connecting

Pass your API token in the connection parameters when establishing the WebSocket:

<%= raw(~S[// JavaScript example using @absinthe/socket
import { Socket as PhoenixSocket } from "phoenix";
import * as AbsintheSocket from "@absinthe/socket";

const phoenixSocket = new PhoenixSocket(
  "wss://app.towerops.net/socket/graphql",
  { params: { token: "your-api-token" } }
);
const absintheSocket = AbsintheSocket.create(phoenixSocket);]) %>

deviceStatusChanged

Subscribe to device status changes. Optionally filter by a specific device ID, or omit to receive all device events in your organization.

Argument Type Description
deviceId ID Optional — filter to a single device
<%= raw(~S[subscription {
  deviceStatusChanged(deviceId: "device-uuid") {
    deviceId
    deviceName
    status
    changedAt
  }
}

# Or subscribe to all devices in your organization:
subscription {
  deviceStatusChanged {
    deviceId
    deviceName
    status
    changedAt
  }
}]) %>

alertEvent

Subscribe to alert lifecycle events (created, acknowledged, resolved) for your organization.

<%= raw(~S[subscription {
  alertEvent {
    alertId
    alertType
    severity
    message
    deviceId
    eventType
    triggeredAt
  }
}]) %>

sensorReadingsUpdated

Subscribe to live sensor reading updates for a specific device. Delivers batched readings as they are polled.

Argument Type Description
deviceId ID! Required device ID
<%= raw(~S[subscription {
  sensorReadingsUpdated(deviceId: "device-uuid") {
    deviceId
    deviceName
    readings {
      sensorId
      sensorType
      sensorDescr
      sensorUnit
      value
      checkedAt
    }
  }
}]) %>

Types Reference

All field names use camelCase in GraphQL queries (Absinthe automatically converts from Elixir's snake_case).

Device

id, name, ipAddress, description, displayOrder, status, lastCheckedAt, lastStatusChangeAt, monitoringEnabled, checkIntervalSeconds, snmpEnabled, snmpVersion, snmpCommunity, snmpCommunitySource, snmpPort, snmpTransport, snmpTransportSource, lastDiscoveryAt, lastSnmpPollAt, snmpv3SecurityLevel, snmpv3Username, snmpv3AuthProtocol, snmpv3PrivProtocol, snmpv3CredentialSource, mikrotikUsername, mikrotikPort, mikrotikSshPort, mikrotikUseSsl, mikrotikEnabled, mikrotikCredentialSource, deviceRole, deviceRoleSource, siteId, organizationId, insertedAt, updatedAt, site

Site

id, name, description, location, displayOrder, snmpVersion, snmpCommunity, snmpPort, snmpTransport, snmpv3SecurityLevel, snmpv3Username, snmpv3AuthProtocol, snmpv3PrivProtocol, mikrotikUsername, mikrotikPort, mikrotikSshPort, mikrotikUseSsl, mikrotikEnabled, organizationId, agentTokenId, parentSiteId, insertedAt, updatedAt

Alert

id, alertType, message, triggeredAt, acknowledgedAt, resolvedAt, emailSentAt, deviceId, acknowledgedById, gaiiaImpact, insertedAt, device, acknowledgedByEmail

Agent

id, name, enabled, isCloudPoller, allowRemoteDebug, lastSeenAt, lastIp, metadata, organizationId, insertedAt, updatedAt, deviceCount

Organization

id, name, slug, subscriptionPlan, useSites, snmpVersion, snmpCommunity, snmpPort, snmpTransport, snmpv3SecurityLevel, snmpv3Username, snmpv3AuthProtocol, snmpv3PrivProtocol, mikrotikEnabled, mikrotikUsername, mikrotikPort, mikrotikSshPort, mikrotikUseSsl, defaultAgentTokenId, insertedAt, updatedAt

Member

id, email, role, isDefault, userId, insertedAt

Integration

id, provider, enabled, syncIntervalMinutes, lastSyncedAt, lastSyncStatus, organizationId, insertedAt, updatedAt

ActivityItem

summary, detail, timestamp, severity, icon, deviceName, siteName, link, type

Sensor

id, sensorType, sensorUnit, sensorDescr, currentValue, lastCheckedAt, monitored

SensorReading

sensorId, value, status, checkedAt

InterfaceTrafficPoint

timestamp, inBps (Float), outBps (Float), inErrors (Int), outErrors (Int)

CheckResultPoint

checkId, value, status, checkedAt, responseTimeMs

DeviceStatusEvent

deviceId, deviceName, status, changedAt

AlertEvent

alertId, alertType, severity, message, deviceId, eventType, triggeredAt

SensorReadingsEvent

deviceId, deviceName, readings (list of SensorReadingSnapshot: sensorId, sensorType, sensorDescr, sensorUnit, value, checkedAt)

Utility Types

DeleteResult: success (Boolean!), message (String)
TestResult: success (Boolean!), message (String)
MetricPoint: timestamp, value, status, checkName, checkType
Interface: id, ifIndex, ifName, ifDescr, ifType, ifSpeed, ifAdminStatus, ifOperStatus, ifAlias
Invitation: id, email, role, expiresAt, acceptedAt, insertedAt
AgentWithToken: id, name, enabled, token, insertedAt

curl Examples

List devices with site info

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "{ devices { id name ipAddress status site { id name location } } }"}'|) %>

Get active alerts

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "{ alerts(status: \"active\") { id alertType message triggeredAt device { name ipAddress } } }"}'|) %>

Create a device

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "mutation { createDevice(input: { ipAddress: \"192.168.1.1\", name: \"Core Router\", snmpEnabled: true }) { id name status } }"}'|) %>

Acknowledge an alert

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "mutation { acknowledgeAlert(id: \"alert-uuid\") { id acknowledgedAt } }"}'|) %>

Using variables

curl -X POST https://app.towerops.net/api/graphql \
  -H "Authorization: Bearer <%= @sample_token %>" \
  -H "Content-Type: application/json" \
  <%= raw(~S|-d '{"query": "query GetDevice($id: ID!) { device(id: $id) { id name ipAddress status site { name } } }", "variables": {"id": "device-uuid"}}'|) %>