Skip to content

API tokens (/api/v1)

What this is

Long-lived Bearer tokens for automating PiHerder from n8n, Home Assistant, or scripts. The browser UI uses session cookies separately.

Why it exists

Operators want “backup host X every night from HA” without storing a human password. Tokens are scoped, rotatable, revocable, and audited — admin only.

Where: Settings → API management (/herder-backups?tab=api) — admin only.
Catalog tab: scopes and endpoints render as a dense list (not a wide table).
Interactive OpenAPI: on your instance at /docs (Swagger — Authorize with Bearer ph_…) and /redoc (ReDoc). Those two paths allow jsDelivr so the stock FastAPI UI can load; the rest of the app stays self-hosted CSP. Settings → API also has a Try a token panel for a quick same-origin smoke check after create/rotate.
Long reference: docs/API.md in the repo.

Auth

Authorization: Bearer ph_<secret>
  • Secret shown once at create or rotate; only a hash is stored.
  • Rotate issues a new secret immediately.
  • Revoke soft-disables; row kept for audit.

Scopes

Scope Allows
read Catalog, health, servers, jobs GET
jobs POST …/servers/{id}/jobs (start work units)
edit PATCH …/features (feature flags)
files Host Files fleet list / download / upload / mkdir / rename / empty-delete — not in default scopes. Privileged, zip, edit, chmod, recursive delete stay UI + 2FA. Richer Files token API is still out of 1.4.
feature:backup Restrict jobs to backup-related types when any feature:* is set
feature:os OS patch / OS update-check jobs (apt or HAOS ha CLI when os_type=haos)
feature:docker Container patch / container update-check / stack check-deploy jobs

If no feature:* scopes are set, any job type allowed by jobs may run (still subject to server feature flags). Prefer least privilege: e.g. n8n backups = read + jobs + feature:backup.

409 / exclusive jobs: a second start of the same exclusive type on a host returns HTTP 409 with the existing job_id (same rule as the UI).

IP allowlist

Optional IPs/CIDRs per token. Enforced using Caddy-forwarded client IP — call via ports 8888/8443.

CORS

Off by default. Server-side n8n/HA/curl do not need CORS. Set CORS_ORIGINS only for browser apps on other origins (exact origins; never *).

Examples

export PH_TOKEN='ph_…'
export PH_URL='https://piherder.example.com'

curl -sS -H "Authorization: Bearer $PH_TOKEN" "$PH_URL/api/v1" | jq .
curl -sS -H "Authorization: Bearer $PH_TOKEN" "$PH_URL/api/v1/servers" | jq .

curl -sS -X POST -H "Authorization: Bearer $PH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"job_type":"backup"}' \
  "$PH_URL/api/v1/servers/1/jobs"

Prefer least privilege: e.g. n8n backup token = read + jobs + feature:backup + n8n host IP.