Conventions
The conventions every Veriticity endpoint follows: minor-unit money as strings, ISO 8601 timestamps, UUID identifiers, pagination, idempotency, body limits and versioning.
Rules that hold across every endpoint. Reading this once saves discovering them one surprise at a time.
Money#
Every monetary amount is a decimal string of ISO 4217 minor units, paired with an alpha-3 currency. Pence for GBP, cents for USD.
Amounts are accepted as a JSON number or a string on input, and always returned as a string — including small ones.
Veriticity performs no currency conversion. A purchase in a currency no applicable policy covers is refused with CURRENCY_NOT_COVERED rather than converted at a rate nobody agreed to.
Time#
Every timestamp is ISO 8601, UTC, with milliseconds: 2026-09-12T14:03:21.114Z.
Fields are null when the thing has not happened rather than absent, so a client’s shape is stable and no key needs probing for.
Identifiers#
Bare UUIDs, with no type prefix. Identifiers that need to sort by creation time — purchase requests, decisions, webhook events — are UUIDv7, so they sort lexicographically by when they were created.
Credentials are the exception and carry a namespace: cmp_live_, cmp_oat_, cmp_ort_, cmp_oac_, cmp_whs_. Those prefixes are permanent.
Enumerations#
Shouted constants: APPROVED, REQUIRES_APPROVAL, ACTIVE, DISABLED, ARCHIVED, REVOKED.
New values may be added. Handle an unrecognised enum value rather than crashing on it — a switch with no default is the thing that breaks when a reason code is added.
Collections and pagination#
There are three pagination models on the API. This is a real inconsistency rather than a subtlety, and it is documented as it is rather than smoothed over.
| Endpoint | Parameters | Response |
|---|---|---|
GET /v1/policies | ?page, ?pageSize | { policies, pagination } |
GET /v1/webhooks/{id}/deliveries | ?limit | { items, hasMore } |
GET /v1/agents | None | { agents } |
GET /v1/webhooks | None | { items } |
MCP list_purchases | limit (1–50), cursor | { purchases, nextCursor, hasMore } |
Page and size, where it applies#
pageSizedefaults to 25 and is clamped to 100 rather than refused — asking for everything gets you the maximum.- A malformed value is refused, because it means your client believes something untrue.
- A page beyond the end returns an empty page, not a
404. paginationis always present, andtotalPagesis at least 1 even when nothing matched.
Request bodies#
Content-Type: application/json on every write, or you get 415. The one exception is POST /v1/webhooks/{id}/rotate-secret, which accepts no body at all.
| Surface | Limit |
|---|---|
| Purchases, policies, simulations | 64 KB |
| Webhook management | 16 KB |
The purchase limit is a real bound rather than a formality: the body is stored verbatim as evidence, so an unbounded one would be an unbounded write into permanent records.
Versioning#
Three version vocabularies, and they mean different things.
| Version | Where | What it versions |
|---|---|---|
/v1 | The URL path | The REST contract. |
2026-09-01 | apiVersion in a webhook envelope | The event contract, pinned per endpoint. A date, because dates sort and do not invite semver reasoning that does not apply. |
veriticity-trust-engine@0.1.0 | engineVersion in a decision | Which evaluator produced this decision. Evidence, not a contract to branch on. |
Additive changes are not breaking. Fields may be added to responses, and webhook event types may be added, at any time. Ignore what you do not recognise.
Known inconsistencies#
These are documented as they are rather than quietly normalised. Changing any of them would break working integrations, so they are recorded here so you can plan around them.
- Collection envelopes differ.
{ policies, pagination },{ agents },{ items },{ items, hasMore }and{ purchase }all appear. There is no single convention. - Three pagination models, plus two unpaginated collections. See the table above.
- There is no
GET /v1/purchase-requests. An MCP connection can enumerate its own purchases; the REST API reads one at a time by id. - Idempotency exists on one operation only. Webhook creation, rotation and test-send accept no idempotency key.
- Body limits differ between 64 KB and 16 KB.
rotate-secretaccepts an absent body where every other write requires a JSON one.- Whole-body validation issues use two spellings of
path—"body"on purchases, an empty string on webhooks. Match on the issuecode, not the path.
The full contract, including these, is in the API reference.