Skip to content

External API ​

The external API gives read access to your fleet from outside the dashboard. Use it for a status page, a customer portal, or your own scripts. Every endpoint is a GET under /api/external/ and authenticates with an API key in the x-api-key header.

API keys ​

Create a key under Settings, API Keys. The full key is shown once, when you create it. Copy it then. The list afterwards shows each key's name, scope and when it was last used. Revoke a key from the same list.

Keys created from the dashboard have the read permission. A key acts as the user who created it. It sees what that user can see, never more.

sh
curl -H "x-api-key: $DAEMONITOR_API_KEY" https://daemonitor.com/api/external/fleet

A missing, unknown or expired key returns 401. A key without the read permission returns 403. Each accepted request updates the key's last-used time.

Organization-scoped keys ​

A key can be limited to one organization. An org-scoped key returns only systems assigned to that organization, on every endpoint. Asking for another system by id returns 404, and /groups lists only groups with a member in the organization, with the other members left out. The scope only narrows. The key still resolves through its user's own access first, so it can't reach a system the user couldn't. Revoking the user's access revokes the key's.

This is the key to hand a customer-facing tool. An unscoped key would show that customer every other customer's systems you own.

The dashboard doesn't offer the scope yet. Create a scoped key with a signed-in request:

POST /api/me/api-keys/create
{ "name": "customer portal", "orgId": "<organization id>" }

You must be a member of the organization.

Which endpoints see shared systems ​

Endpoints fall into two groups.

VisibilityEndpoints
Systems the key's user owns/systems, /systems/:systemId
Everything the user can see, including shares and organization membership/fleet, /fleet/:systemId, /systems/:systemId/history, /groups, /incidents

For a system the user received through a group share, the second group returns only the services in that group.

/fleet, /fleet/:systemId, /systems and /systems/:systemId leave out archived systems and archived services.

GET /api/external/fleet ​

The whole visible fleet, with each system's services normalised into one shape. Use this rather than /systems unless you need the raw plugin data. The normalised shape stays stable when a plugin's own payload changes.

json
{
  "version": 1,
  "generatedAt": "2026-10-14T09:00:00.000Z",
  "systems": [
    {
      "id": "3f0c...",
      "name": "web-01",
      "status": "online",
      "last_seen": "2026-10-14T08:59:41.000Z",
      "services": [ /* Service[] */ ]
    }
  ]
}

Each system object carries the system's own fields plus services. The system's ingest key is never included, on this or any other endpoint.

The Service object ​

The main fields:

FieldTypeMeaning
idstringThe service id.
namestringDisplay name.
stackstringThe id of the system it runs on.
kindLabel, badgestringWhat kind of service it is, for display.
levelok, warn, crit, standbyCurrent level. crit also covers a service that stopped reporting.
statusWordstringThe one-line status, such as OFFLINE · 12m or a reason from the server.
statusReasonstringThe server's reason, when there is one.
cpu, memnumberPercentages, where the service has them.
pingnumberLatency in ms, where the service has it.
agenumberSeconds since the last report.
unitsarraySub-items, such as containers in a compose project.
metricsarrayExtra stat tiles.
sparkline, loadnumber[]Recent samples for small charts.
pluginsstring[]Plugins behind this service.
lastReportstringTime of the last report, preformatted.
isGroup, isClusterbooleanSet on rollup items.

A host that is correctly not running a clustered service has level: "standby". The cluster rollup card itself is not included. See Clusters.

The full type is Service in @daemonitor/common, types/fleet.ts.

GET /api/external/fleet/:systemId ​

One visible system, normalised the same way. Returns 404 for a system the key can't see, so a caller can't probe for other people's system ids.

json
{
  "version": 1,
  "generatedAt": "2026-10-14T09:00:00.000Z",
  "system": { "id": "3f0c...", "name": "web-01" },
  "services": [ /* Service[] */ ]
}

The system and its services are siblings here. In /fleet the services sit inside each system.

GET /api/external/systems ​

Systems owned by the key's user, each with its raw service rows. Shared systems are not included.

Returns an array:

json
[
  {
    "id": "3f0c...",
    "name": "web-01",
    "status": "warning",
    "status_reason": "Memory usage is high",
    "states": [
      {
        "id": "a81b...",
        "unique_id": "os",
        "type": "os",
        "status": "warning",
        "status_reason": "Memory usage is high",
        "data": { },
        "updated_at": "2026-10-14T08:59:41.000Z"
      }
    ]
  }
]

states holds one row per service, newest report first. data is the plugin's last report exactly as the agent sent it, so its shape depends on the plugin.

GET /api/external/systems/:systemId ​

One owned system with its states, in the same shape as one element of /systems. Returns 404 for a system the user doesn't own or that is archived.

GET /api/external/systems/:systemId/history ​

A short time series for one service.

QueryMeaning
stateRequired. The service id, the id of a row in states.
limitNumber of points returned. Default 60, maximum 240.
json
{
  "points": [12, 14, 13],
  "label": "CPU",
  "unit": "%",
  "cpu": [12, 14, 13],
  "mem": [61, 61, 62]
}

points is the primary series for the service type. For os it is CPU in percent and mem carries memory in percent. For web it is response time in ms. For Cloudflare projects it is request counts. For containers and pm2 it is CPU. The arrays run oldest to newest.

A state the key can't see returns empty arrays rather than an error. A system it can't see returns 404.

GET /api/external/groups ​

Groups visible to the key's user. That includes their own, those of any account delegated to them, and groups shared with them directly. Sorted by sort.

json
[
  {
    "id": "9c2d...",
    "name": "Storefront",
    "description": "Public shop and its API",
    "color": "#2f81f7",
    "sort": 0,
    "user_id": "5e1a...",
    "kind": "website",
    "cf_zone": "example.com",
    "members": ["a81b...", "c03f..."]
  }
]

members lists service ids, the same ids /fleet returns. A member id outside the services your /fleet call returned resolves to nothing.

kind is free text set in the dashboard, or null. cf_zone is the Cloudflare zone linked to the group, or null.

GET /api/external/incidents ​

Incidents marked client-visible that name at least one system the key can see. Returns at most 200, newest start first.

QueryMeaning
sinceISO timestamp. Only incidents updated at or after it. Lets a poller fetch just what changed.
openAny value. Only incidents with no end yet.
json
{
  "version": 1,
  "generatedAt": "2026-10-14T09:00:00.000Z",
  "incidents": [
    {
      "id": "7d41...",
      "title": "Checkout errors",
      "severity": "crit",
      "status": "monitoring",
      "startedAt": "2026-10-14T02:10:00.000Z",
      "endedAt": null,
      "summary": "Payment API timed out under load.",
      "solution": null,
      "updatedAt": "2026-10-14T03:05:00.000Z",
      "targets": [
        { "type": "system", "id": "3f0c...", "label": "web-01", "systemId": "3f0c..." }
      ],
      "updates": [
        { "id": "e9a0...", "kind": "status", "body": "Fix deployed, watching error rate.", "url": null, "status": "monitoring", "at": "2026-10-14T03:00:00.000Z" }
      ]
    }
  ]
}

targets leaves out systems the key can't see. updates contains only client-visible timeline entries, oldest first, and never says who wrote them.