Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

API examples

The endpoints a service uses, with real requests and responses. The full endpoint list is in the generated API reference.

Base URL: https://shriek.dev. Bodies are JSON (Content-Type: application/json); times are Unix seconds. Every /v1/ example sends a ChirpAuth machine token; see Authentication. $AUTH below stands for:

AUTH=(-H "Authorization: Bearer $SHRIEK_TOKEN" -H "Content-Type: application/json")

A source is always an object with two fields, each 1 to 256 bytes:

{"service": "billing", "component": "nightly-export"}

Post an alert

POST /v1/shriek/alert

curl https://shriek.dev/v1/shriek/alert "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "deploy"},
  "severity": "warn",
  "title": "deploy rolled back",
  "detail": "health check failed on 2 of 3 hosts; rolled back to v41"
}'
FieldRequiredRules
sourceYesA source
severityYesinfo, warn or critical. critical notifies phones.
titleYes1 to 256 bytes
detailNoUp to 4096 bytes. Defaults to "".

Response, 200 OK, the recorded alert:

{
  "source": {"service": "billing", "component": "deploy"},
  "severity": "warn",
  "title": "deploy rolled back",
  "detail": "health check failed on 2 of 3 hosts; rolled back to v41",
  "at": 1793911347
}

Each call appends a new alert, including a retry of the same body.

Register a source

POST /v1/shriek/expectations

curl https://shriek.dev/v1/shriek/expectations "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "nightly-export"},
  "period_secs": 3600
}'
FieldRequiredRules
sourceYesA source
period_secsYesAt least 60
sinceNoUnix seconds. The first deadline is since + period_secs. Defaults to now.

Response, 200 OK. Note the period comes back as period:

{
  "source": {"service": "billing", "component": "nightly-export"},
  "period": 3600,
  "since": 1793911347
}

Send a heartbeat

POST /v1/shriek/heartbeat

curl https://shriek.dev/v1/shriek/heartbeat "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "nightly-export"}
}'
{"ok": true, "at": 1793911347}

404 if the source is not registered.

List sources

GET /v1/shriek/expectations lists every registered source with its last heartbeat and status:

[
  {
    "source": {"service": "billing", "component": "nightly-export"},
    "period_secs": 3600,
    "since": 1793911347,
    "last_seen": 1793911347,
    "status": {"state": "firing", "since": 1793914953}
  }
]

last_seen is null before the first heartbeat. status is {"state": "ok"}, or {"state": "firing", "since": <when it started>}.

Retire a source

DELETE /v1/shriek/expectations with the source in the body:

curl -X DELETE https://shriek.dev/v1/shriek/expectations "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "nightly-export"}
}'
{
  "retired": true,
  "source": {"service": "billing", "component": "nightly-export"}
}

404 if the source isn’t registered (never was, or is already retired). See Dead-man’s switches for what retiring does.

Read the board

GET /v1/shriek/board

[
  {
    "service": "billing",
    "health": "red",
    "firing": [{"service": "billing", "component": "nightly-export"}]
  }
]

Read the feed

GET /v1/shriek/alerts returns every alert, oldest first. Add ?since=<unix seconds> for alerts recorded at or after that time. GET /v1/shriek/alerts/{service} returns one service’s alerts.

[
  {
    "source": {"service": "billing", "component": "deploy"},
    "severity": "warn",
    "title": "deploy rolled back",
    "detail": "health check failed on 2 of 3 hosts; rolled back to v41",
    "at": 1793911347
  },
  {
    "source": {"service": "billing", "component": "payments"},
    "severity": "critical",
    "title": "card payments failing",
    "detail": "processor returns 503 for every charge",
    "at": 1793911347,
    "incident": {"id": "billing/payments#processor-503", "event": "opened"}
  },
  {
    "source": {"service": "billing", "component": "payments"},
    "severity": "info",
    "title": "resolved",
    "detail": "incident `card payments failing` resolved after 1s",
    "at": 1793911348,
    "incident": {"id": "billing/payments#processor-503", "event": "resolved"}
  },
  {
    "source": {"service": "billing", "component": "nightly-export"},
    "severity": "critical",
    "title": "no signal",
    "detail": "expected every 3600s; no signal from `billing/nightly-export` for 3606s",
    "at": 1793914953
  }
]

incident is present only on alerts that open or resolve an incident. The feed is not paged: a call returns everything that matches.

Open an incident

POST /v1/shriek/incidents

curl https://shriek.dev/v1/shriek/incidents "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "payments"},
  "severity": "critical",
  "title": "card payments failing",
  "detail": "processor returns 503 for every charge",
  "dedup_key": "processor-503"
}'
FieldRequiredRules
source, severity, title, detailAs for an alert
dedup_keyNoMakes the id {service}/{component}#{dedup_key}, which must fit in 256 bytes. Without it the id is a random UUID.

Response, 200 OK, the open incident. Sending the same request again while it is open returns the same body and records nothing:

{
  "id": "billing/payments#processor-503",
  "source": {"service": "billing", "component": "payments"},
  "severity": "critical",
  "title": "card payments failing",
  "since": 1793911347
}

GET /v1/shriek/incidents lists the open incidents in this shape.

Resolve an incident

POST /v1/shriek/incidents/resolve

curl https://shriek.dev/v1/shriek/incidents/resolve "${AUTH[@]}" -d '{
  "id": "billing/payments#processor-503"
}'
{"resolved": true, "id": "billing/payments#processor-503", "down_for_secs": 1}

404 if no incident with that id is open.

Publish a component

POST /v1/shriek/public/components puts a registered source on your public board under a label of 1 to 256 bytes:

curl https://shriek.dev/v1/shriek/public/components "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "nightly-export"},
  "label": "Data exports"
}'
{"source": {"service": "billing", "component": "nightly-export"}, "label": "Data exports"}

404 if the source isn’t a registered expectation (never registered, or retired). Retiring a source also takes its component off the board.

GET /v1/shriek/public/components lists them in this shape.

DELETE /v1/shriek/public/components takes one off the board. A service with no components left leaves the board, and its updates leave the page:

curl -X DELETE https://shriek.dev/v1/shriek/public/components "${AUTH[@]}" -d '{
  "source": {"service": "billing", "component": "nightly-export"}
}'
{"removed": true, "source": {"service": "billing", "component": "nightly-export"}}

404 if no component is published for that source. Publishing it again puts it back.

Post a public update

POST /v1/shriek/public/updates; message is 1 to 256 bytes:

curl https://shriek.dev/v1/shriek/public/updates "${AUTH[@]}" -d '{
  "service": "billing",
  "severity": "critical",
  "message": "Card payments are failing. We are working with our processor."
}'
{
  "service": "billing",
  "severity": "critical",
  "message": "Card payments are failing. We are working with our processor.",
  "at": 1793911348
}

Anyone can read it, and it can’t be edited or removed.

Read the public status page

No credential. For a tenant named acme:

curl -fsS https://shriek.dev/public/acme/board
curl -fsS https://shriek.dev/public/acme/billing/updates

With the component and update above, and the incident still open, these return:

[
  {
    "service": "billing",
    "health": "red",
    "components": [{"label": "Payments", "health": "red"}]
  }
]
[
  {
    "service": "billing",
    "severity": "critical",
    "message": "Card payments are failing. We are working with our processor.",
    "at": 1793911348
  }
]

Updates are listed oldest first. The HTML page is at https://shriek.dev/public/acme.