Version 1.0.0 · REST API for CI, scripts and integrations.
The public API v1 of Moatgator. This file is the source of truth: the server validates requests and (outside production) responses against it, src/publicapi/types.gen.ts is generated from it, and the reference page is built from it.
Authentication
Send an API key as Authorization: Bearer mg_live_... (or mg_test_...). A key belongs to one app and carries scopes (builds:write, builds:read, scans:read, threats:read, attest:verify); ask only for what the integration needs. For CI there is no key to store: a GitHub Actions job exchanges its own OIDC token for a short-lived access token (POST /v1/auth/oidc, valid 10 minutes) and sends it as Authorization: Bearer <token> in place of the key. The app must have a trust rule that names the repository and ref (or environment) allowed to do so; with no rule, nobody gets in. Access tokens never carry device scopes (attest:verify, events:ingest).
Errors
Errors use RFC 9457application/problem+json. code is a stable machine-readable identifier; detail is for people.
Rate limits
Per API key, by the plan of the app. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the window resets); a 429 also carries Retry-After.
Idempotency
POST operations that create something accept an Idempotency-Key header (8 to 128 characters). Repeating the same key with the same body within 24 hours returns the original response (with Idempotent-Replayed: true); the same key with a different body is a 422.
Pagination
List endpoints use opaque cursors: pass next_cursor from the previous page as cursor. Never build or parse a cursor.
Versioning
The major version is in the path (/v1). Additive changes (new fields, new endpoints) are not breaking. Deprecated operations send Deprecation and Sunset headers at least 6 months before removal. See docs/api/versioning.md.
Auth
POST/v1/auth/oidc
Exchange a CI OIDC token for an access token· No API key (signed URL)
In a GitHub Actions job with permissions: id-token: write, request an OIDC token with audience equal to this API's base URL and send it here with the id of the app. If a trust rule of the app matches the token (issuer, repository, ref or environment), the response is a short-lived access token (10 minutes) with the scopes granted by the matching rules. An OIDC token can be exchanged once. The same 401 answers every refusal (unknown app, no rule, wrong repository, expired, replayed token): the reason is recorded in the organization's audit trail, not revealed to the caller.
Identifies the credential (API key or OIDC access token) without needing any scope: the app it belongs to, the plan, the scopes and when it expires. Used by moatgator whoami and moatgator doctor. Never returns the secret.
Registers a build of the app that owns the API key. Declare the artifact (sizeBytes, sha256) to receive a short-lived signed uploadUrl for the file. Requires scope builds:write.
Uploads the file declared when the build was created, as the raw request body (application/octet-stream). The URL is the signed uploadUrl returned by createBuild; the token in the path is the credential, so it needs no Authorization header and expires in 15 minutes. The size and SHA-256 must match what was declared.
Checks the attestation token an app sent to your backend. Requires scope attest:verify. The attestation backend is a stub in this version (engine: stub): the contract is stable, the verdicts are not yet backed by real platform checks.