Appearance
Errors
| Status | Body | Means |
|---|---|---|
401 | {"error": "unauthorized"} | Missing, malformed, unknown, revoked or expired key |
403 | {"error": "forbidden"} | Authenticated, but the key's scope lacks the permission |
404 | {"error": "not_found"} | No such record in this account |
422 | {"errors": {"field": ["message"]}} | Validation failed |
404 covers two cases on purpose
A record that does not exist and a record belonging to another account both return 404, with byte-identical bodies.
Returning 403 for the second would confirm that an id exists somewhere in the system, and walking that difference enumerates another tenant's portfolio. The tests assert the two responses are identical rather than merely both non-200.
So: a 404 means not available to this key, and you cannot conclude anything more.
403 versus 404
403 is reserved for a resource your key could see but is not permitted to act on — a permission problem within your own account. Anything outside the account is 404.
Validation errors
Keyed by field, with a list of messages:
json
{
"errors": {
"registration_number": ["must be a 10-digit Saudi unified number starting with 7"],
"country": ["is not configured for this account"]
}
}Pagination
An out-of-range page is not an error: the list comes back empty with accurate meta, so a caller walking pages can stop on an empty result rather than on an exception. A nonsense page or per_page falls back to the default instead of failing.
Retries
Verity does not currently rate-limit the API. Provider calls behind POST /api/v1/entities can be slow, since creating an entity runs its configured intake checks synchronously — allow a generous timeout rather than retrying aggressively, because a retry starts the provider calls again.