Docs / API reference

API reference

The part of SafeGrd's /api/v1 an agent needs: read backups and Fire Drills, and ask for a backup or a drill. Authenticate with a personal access token from the console (Tokens). A personal access token cannot delete, disable, re-route or re-key anything, on any route. The same operations are served as MCP tools at https://safegrd.dev/mcp; see https://safegrd.dev/docs/mcp.

This page is rendered from /openapi.json, the document an agent reads, so the two cannot disagree. Requests go to https://safegrd.dev, with Authorization: Bearer sg_pat_… from Tokens in the console. Every other /api/v1 route (sign-in, billing, members, sinks, keys, node enrolment) is internal: it changes between releases, and a personal access token is refused on it.

Operations

The plan catalogue: prices, quotas, drill cadence, hosted storage

GET /api/v1/plans (no token needed)
ResponseMeaningBody
200Every planarray

What a backup host's key needs in your own bucket, per provider: steps, least-privilege policy, and for AWS a CloudFormation and a Terraform template that create the bucket with Object Lock

GET /api/v1/storage/bucket-setup (no token needed)
ParameterInMeaning
provider (required)querystring
bucket (required)querystring
expiryquery1 to add what opt-in expiry in your own bucket needs
ResponseMeaningBody
200The setupobject
400An unknown provider or a bucket name S3 does not allow

Organizations the caller belongs to, with plan and quotas

GET /api/v1/orgs
ResponseMeaningBody
200Organizationsarray
401Refused. The body says why.Error

Fire Drill statistics across an organization

GET /api/v1/orgs/{org_id}/drill-stats
ParameterInMeaning
org_id (required)pathstring
ResponseMeaningBody
200Statisticsobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

An organization's projects

GET /api/v1/orgs/{org_id}/projects
ParameterInMeaning
org_id (required)pathstring
ResponseMeaningBody
200Projectsarray
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

Each surface's recovery point and restore time, measured from its snapshots and drills

GET /api/v1/orgs/{org_id}/recovery
ParameterInMeaning
org_id (required)pathstring
ResponseMeaningBody
200One row per surface, with the window and sample counts behind each figureobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

Hosted storage: whether it is offered, the quota, and how much is locked

GET /api/v1/orgs/{org_id}/storage
ParameterInMeaning
org_id (required)pathstring
ResponseMeaningBody
200Usageobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

Protected surfaces and hosts, with last backup, drill and alert state. Each carries last_snapshot, its newest backup's summary

GET /api/v1/nodes
ParameterInMeaning
org_idqueryOnly this organization, which must be one of the caller's
project_idqueryOnly this project's surfaces
limitqueryAsk for a page of this many, newest first (default 50, at most 200). With limit or cursor the response is a Page; without either it is the whole list as a bare array.
cursorquerynext_cursor from the previous page. Opaque; a key, not an offset, so rows arriving meanwhile never repeat or skip one.
ResponseMeaningBody
200Surfaces
400Refused. The body says why.Error
401Refused. The body says why.Error
403Refused. The body says why.Error

One surface or host

GET /api/v1/nodes/{node_id}
ParameterInMeaning
node_id (required)pathstring
ResponseMeaningBody
200The surfaceobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

Ask for a backup of the surface now

POST /api/v1/nodes/{node_id}/backup-now

The host's daemon runs it at its next check-in. A surface SafeGrd backs up is queued on SafeGrd instead (202 with a job_id). Every backup is locked and cannot be deleted before its retention expires, so a request within an hour of the last backup is refused with 429. A host that does not run the daemon cannot pick a request up (409).

ParameterInMeaning
node_id (required)pathstring
ResponseMeaningBody
200Already requestedobject
202Requestedobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error
409Refused. The body says why.Error
429Refused. The body says why.Error

Ask for a Fire Drill of the surface's latest snapshot now

POST /api/v1/nodes/{node_id}/drill-now

The host's daemon runs it at its next check-in. A surface set to drill on SafeGrd is queued there instead (202 with a job_id) and needs no daemon. Counted against the plan's drill cadence: refused with 429 until the next drill is due. Refused with 409 when nothing would run it: no daemon on the host, or no backup yet.

ParameterInMeaning
node_id (required)pathstring
ResponseMeaningBody
200Already requestedobject
202Requestedobject
401Refused. The body says why.Error
402Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error
409Refused. The body says why.Error
429Refused. The body says why.Error

A surface's snapshots: when, how big, locked until when, whether a drill verified them

GET /api/v1/snapshots
ParameterInMeaning
node_idqueryThe surface. Without paging this is its whole history, which `safegrd verify` walks to check the attestation chain.
org_idqueryOnly this organization, which must be one of the caller's
project_idqueryOnly this project's surfaces
limitqueryAsk for a page of this many, newest first (default 50, at most 200). With limit or cursor the response is a Page; without either it is the whole list as a bare array.
cursorquerynext_cursor from the previous page. Opaque; a key, not an offset, so rows arriving meanwhile never repeat or skip one.
ResponseMeaningBody
200Snapshots
400Refused. The body says why.Error
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

One snapshot's recorded metadata: digests, counts, lock, Threat Shield verdict

GET /api/v1/snapshots/{snapshot_id}
ParameterInMeaning
snapshot_id (required)pathstring
ResponseMeaningBody
200The snapshotobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

A surface's Fire Drill reports, with assertions and signatures

GET /api/v1/verifications
ParameterInMeaning
node_idqueryThe surface. Without paging this is its whole history, which `safegrd verify` walks to check the attestation chain.
org_idqueryOnly this organization, which must be one of the caller's
project_idqueryOnly this project's surfaces
limitqueryAsk for a page of this many, newest first (default 50, at most 200). With limit or cursor the response is a Page; without either it is the whole list as a bare array.
cursorquerynext_cursor from the previous page. Opaque; a key, not an offset, so rows arriving meanwhile never repeat or skip one.
ResponseMeaningBody
200Fire Drill reports
400Refused. The body says why.Error
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

One Fire Drill's whole record, the surface it drilled, and its public certificate if one is published

GET /api/v1/verifications/{verification_id}
ParameterInMeaning
verification_id (required)pathstring
ResponseMeaningBody
200The drillobject
401Refused. The body says why.Error
403Refused. The body says why.Error
404Refused. The body says why.Error

A published drill certificate: a statement, and an Ed25519 signature over the statement's bytes. Needs no token.

GET /api/v1/certificates/{certificate_id} (no token needed)
ParameterInMeaning
certificate_id (required)pathThe id in the certificate's /c/ link
ResponseMeaningBody
200The signed statementobject
404Refused. The body says why.Error

Counts of surfaces, snapshots, drills and open anomalies for the caller's scope

GET /api/v1/summary
ParameterInMeaning
org_idqueryOnly this organization, which must be one of the caller's
project_idqueryOnly this project's surfaces
ResponseMeaningBody
200CountsSummary
401Refused. The body says why.Error
403Refused. The body says why.Error

Bodies

Lists come back newest first. With limit or cursor a list is a Page; without either it is the whole list as a bare array.

Error

FieldTypeMeaning
error (required)string

Page

One page of a list, newest first. next_cursor is absent on the last page.

FieldTypeMeaning
items (required)array
next_cursorstring

Summary

Counts for the caller's scope, as the console's tiles show them.

FieldTypeMeaning
drillsinteger
drills_passedinteger
flagged_surfacesinteger
nodesinteger
open_anomaliesinteger
snapshotsinteger
surfacesinteger

The same operations are MCP tools (AI agents). Everything an agent can do from the command line is in the command reference.