Request format
Requests are JSON with Content-Type: application/json. Bodies are validated by Pydantic models; unknown or wrong-typed fields fail validation with 422. The assessment endpoints share one convention, an evidence object (a serialized EvidenceEnvelope), defaulting to {}:
http
POST /api/v1/trust/assess
{ "evidence": { /* EvidenceEnvelope.to_dict() */ } }
POST /api/v1/compliance/assess
{ "pack_id": "eu_ai_act", "evidence": { /* … */ } }
Query parameters are used for listing endpoints (limit, offset, artifact_type). Path parameters carry resource ids (e.g. /api/v1/registry/agents/{agent_id}).
Response format
Responses are JSON. Each capability returns its full export dict (the object's to_dict()). Responses evolve additively: new fields may appear; existing fields keep their meaning. Listing endpoints wrap results with counts:
json
{ "items": [ /* … */ ], "total": 12, "limit": 50, "offset": 0 } // /api/vai/*
{ "agents": [ /* … */ ], "count": 3 } // /api/v1/registry/agents
Error format
Errors use FastAPI's standard shape. A raised HTTPException serializes to a detail string:
json
{ "detail": "Authentication required" }
Request-validation failures (422) return a structured detail array, one entry per invalid field:
json
{
"detail": [
{ "loc": ["body", "input"], "msg": "field required", "type": "value_error.missing" }
]
}
Status codes
| Code | Meaning in VerifAIer |
|---|---|
200 | OK, assessments, audit, listings, control-center overview |
201 | Created, /validate, /pipeline, /dev/audit |
204 | No Content, /api/auth/logout |
400 | Bad request, e.g. unknown artifact_type on /api/vai/artifacts |
401 | Unauthorized, missing/invalid session or API key ("Authentication required" / "Invalid or expired session" / "Invalid credentials") |
403 | Forbidden, API key valid but revoked; org-access denied |
404 | Not found, unknown agent, artifact, validation or run id |
422 | Unprocessable Entity, request-body validation failed (includes unknown pack_id via its Literal) |
429 | Too Many Requests, login rate limit exceeded |
502 | Bad Gateway, /validate / /sentinel/verify when the validation engine raises |
503 | Service Unavailable, e.g. Stripe webhook when the webhook secret is unconfigured |
/api/v1/audit/conversation returns 200 with a deterministic mock-backed result even when a provider is down, failure surfaces as data (status:"error", fallback_used:true), not as a 5xx. See failure handling.Idempotency
There is no Idempotency-Key header. Idempotency instead comes from determinism:
- The assessment / passport / reputation / signal endpoints are pure functions of their input, the same
evidenceyields the same content-derived ids (agent_ pass_ rep_ sig_) and the same result. Re-POSTing is safe and side-effect-free. POST /api/v1/registry/observeis idempotent per agent: it upserts a per-agent entry (created_at preserved, updated_at advances,Nonevalues never clobber). Re-observing the same agent updates in place rather than duplicating.- Only the timestamp (
created_at) varies between identical calls; it is deliberately excluded from content-derived id hashes.
Retries
- Safe to retry: all GETs, and the deterministic POST assessment endpoints (no side effects).
registry/observeis safe to retry because it upserts. - Rate-limited:
POST /api/auth/login: back off on429(see below). - No server-side provider retry. The Provider Router does not retry a failed call, fallback is driven by availability, and the chain is walked at most once. A client that wants cross-attempt retry must implement it itself (exponential backoff on network errors); the API's own responses are already deterministic and safe to replay.
Timeouts
- The application does not impose a per-request wall-clock timeout in code. Set request timeouts at your deployment gateway / client.
- The one upstream call that can block, a live Gemini audit, is bounded inside
sentinel/gemini_proxy(stdliburllibwith a timeout) and self-falls-back, so a slow provider cannot hang the evidence path indefinitely. - Client guidance: the SDK request builders are transport-agnostic; supply your HTTP client's timeout (the dashboard client uses an
AbortControllerwith a default 8s timeout).
Pagination
Pagination is limit/offset where implemented, and is documented per endpoint, it is not uniform, so this table is the source of truth:
| Endpoint | Pagination |
|---|---|
GET /api/vai/artifacts, /api/vai/receipts | limit (1–200, default 50) + offset (≥0) |
GET /validations, /pipeline/runs | limit (1–100, default 20) + offset (≥0) |
GET /api/v1/registry/agents | none: returns all entries with a count |
GET /api/v1/control-center/overview | none: full fleet aggregate |
/api/v1 listing endpoints return complete collections without pagination. For large fleets, aggregate/paginate at your data layer or gateway, cursor pagination is not implemented in the app.Rate limit posture
- Implemented: a per-IP login rate limit via an in-process
RateLimiter:RATE_LIMIT_LOGIN_RPWattempts (default 20) perRATE_LIMIT_LOGIN_WINDOWseconds (default 900 = 15 min). Exceeding it returns429 Too many login attempts. - Not implemented: there is no general per-endpoint or per-tenant rate limiting on the governance or data APIs.