Create a recurring audit.
An audit sweeps a window of your events on a schedule and files findings for
the failure patterns it sees. schedule_interval_secs must be between 3600
(1h) and 604800 (7d) and defaults to daily; lookback_window_secs must be
between 3600 (1h) and 7776000 (90d) and defaults to 7d. schedule_anchor
fixes the phase — runs land on anchor + N * interval — and defaults to the
next 09:00 UTC; an anchor more than 365 days out is rejected. The first run
is queued immediately on create regardless of the anchor.
Authorizations
A scoped AgentEye API key. Mint one in the dashboard under Settings → API keys, or with POST /v1/keys. Each endpoint names the permission it requires; a key without it gets 403 and a required_permission field naming what was missing.
Body
NOT writable here — see PUT /audits/{id}/context.
Present ONLY so a client that sends it gets a loud 422 instead of serde's default of silently ignoring an unknown field, which is how a whole feature goes missing with a green test suite.
Reference context to store WITH the definition. Create only — an update sending it gets the same 422 as the two fields above.
It lives here for one reason, and the reason is a race, not convenience:
the insert trigger queues an eval job whose next_attempt_at defaults to
now(), so a new enabled audit is due the instant its row commits. A
client that created the audit and then wrote its context in a second
request could be overtaken by the dispatcher, and the first run — the one
an operator watches — would argue without the brief they just typed.
Writing both in one transaction removes the window rather than narrowing
it: the job row and the context rows become visible together.
Why an update must NOT accept it: _audit_to_body (CLI) and
auditToInput (dashboard) rebuild this whole body from an allowlist
before every save, so a context field on the update path would be wiped
by any unrelated edit — 200 OK, no error, brief gone. Create has no
read-merge step and therefore no such hazard. See the module comment in
routes/audit_context.rs.
NOT writable here — see PUT /audits/{id}/context.
Fixed phase for the schedule: runs land on anchor + N * interval.
Omitted on create → default_schedule_anchor() (next 09:00 UTC, matching
the 09:00 backfill in 20260721000200). Omitted on update → unchanged.
NULL in the DB is still tolerated by the dispatcher (legacy drift path)
but nothing this route writes can produce it any more.
Response
Audit created. Returns id and created_at.

