Core API - overview and authentication
Base URL, versioning, and the two authentication models: API key for agents, session for the dashboard and integrators.
The Core API is the single HTTP surface that guard-agent instances and the dashboard both talk to. Everything documented in the reference section lives under one base URL and one versioned prefix.
Base URL and versioning
All endpoints share a single base URL and are mounted under the /api/v1 prefix:
https://api.guard-core.com/api/v1The prefix is defined once in backend/guard-core-api/guard_core_api/app.py and applied to every router at startup. All paths in the Agent Telemetry API therefore start with /api/v1.
Every response carries an API-Version header set to the running release's CalVer build (for example 2026.08.28.3). The dashboard captures the first value it sees after a page load and only prompts for a refresh when a later response reports a different one.
Two authentication models
The API exposes two distinct surfaces, agent ingestion and session-authenticated management, and each uses a different credential mechanism:
| Surface | Credential | Header |
|---|---|---|
| Agent ingestion (events, metrics, status, rules) | Project API key | X-API-Key |
| Dashboard and integrator (projects, keys, encryption) | Session JWT (cookie or Authorization bearer) | Set by fastapi-users |
Never mix the two: sending an API key to a session endpoint returns 401. On an ingestion endpoint, omitting the X-API-Key header entirely returns 422 because the header is required, while sending session credentials (or any invalid value in X-API-Key) returns 401 because the telemetry router validates the key directly.
API key auth (agents)
Ingestion endpoints require an X-API-Key header whose value is a project-scoped API key. An optional X-Project-Id header disambiguates when one key covers multiple projects, but is not required if the key already maps to a single project.
The key is validated by authenticate_telemetry_project in guard_core_api/services/telemetry_service.py, which delegates to verify_api_key in guard_core_api/services/api_keys_service.py. An invalid or missing key raises HTTP 401 Unauthorized immediately, before any other processing occurs.
Session auth (dashboard and integrators)
The SaaS dashboard authenticates via a session obtained at login. This session surface exists to power the dashboard UI. Operations such as managing projects, issuing or revoking API keys, and rotating per-project encryption are performed through the dashboard rather than by scripting against raw endpoints.
The public, documented programmatic surface is the agent telemetry API only. Agents authenticate with an X-API-Key header (see the section above); they do not use session credentials.
Rate limits and quotas
Ingestion (write) endpoints are protected by at least one layer of rate limiting. Read-only endpoints such as GET /api/v1/status carry no rate-limit bucket:
- Per-IP burst bucket - applied on ingestion endpoints before the API key is verified. If the sending IP exhausts its token bucket the server returns
429 Too Many Requestswith aRetry-Afterheader. - Per-API-key bucket - capacity varies by organization tier (
individual,pro,team,business,enterprise). - Per-project bucket - a second sliding window scoped to the project, also tier-dependent.
- Session endpoint rate limits - session-authenticated endpoints are rate-limited by guard's
@rate_limitdecorator (typically 10-30 requests per 60-second window).
When an agent receives a 429 it should not drop the payload. The agent buffering guide explains the retry and back-off strategy guard-agent uses to survive rate-limit windows without losing events.
How this reference stays accurate
The API catalog at frontend/src/content/docs/_generated/api-catalog.json is generated directly from the live FastAPI OpenAPI schema by running scripts/emit_api_catalog.py in the backend. CI regenerates the catalog on every push and fails the build if the committed file drifts from the live schema.
Every api-endpoint HTML comment in these reference docs is checked by npm run lint:docs:api-catalog against that catalog. If a documented path no longer exists in the API, the lint fails and the doc must be updated before the branch can merge. This makes the reference docs machine-checked rather than trust-based.