API reference
The complete Veriticity REST API: every endpoint, its credential requirements, scopes, parameters, request and response shapes, and the errors it can return.
18 operations across 5 groups. Everything on this page is generated from the Veriticity OpenAPI document, which you can download and feed to a client generator or an HTTP client:
openapi.json — OpenAPI 3.1, 18 operations, including the outbound webhook contract.
Endpoints at a glance#
| Method | Path | Credential |
|---|---|---|
| POST | /v1/purchase-requests | API key bound to an agent · purchase:request |
| GET | /v1/purchase-requests/{purchaseRequestId} | API key bound to an agent · purchase:read |
| POST | /v1/simulations/current-policies | API key of either kind · purchase:check |
| POST | /v1/simulations/draft-policy | Organisation-scoped API key |
| POST | /v1/simulations/policy-version | Organisation-scoped API key |
| GET | /v1/policies | Organisation-scoped API key |
| GET | /v1/policies/{policyId} | Organisation-scoped API key |
| GET | /v1/policies/{policyId}/versions/{versionId} | Organisation-scoped API key |
| POST | /v1/policies/{policyId}/versions/{versionId}/validate | Organisation-scoped API key |
| GET | /v1/agents | Organisation-scoped API key |
| GET | /v1/webhooks | Organisation-scoped API key |
| POST | /v1/webhooks | Organisation-scoped API key |
| GET | /v1/webhooks/{webhookId} | Organisation-scoped API key |
| PATCH | /v1/webhooks/{webhookId} | Organisation-scoped API key |
| DELETE | /v1/webhooks/{webhookId} | Organisation-scoped API key |
| GET | /v1/webhooks/{webhookId}/deliveries | Organisation-scoped API key |
| POST | /v1/webhooks/{webhookId}/rotate-secret | Organisation-scoped API key |
| POST | /v1/webhooks/{webhookId}/test | Organisation-scoped API key |
Purchases#
Submit a purchase for a decision, and find out what happened to it.
Submit a purchase for a decision#
POST/v1/purchase-requests
API key bound to an agentOAuth: purchase:request
Asks the Trust Engine whether this agent may make this purchase, and records the answer.
All three outcomes — `APPROVED`, `REJECTED` and `REQUIRES_APPROVAL` — are successful calls. Branch on `decision.outcome`, never on the HTTP status code: the status describes whether the question could be answered, and a refusal is an answer.
`REQUIRES_APPROVAL` means a person has been asked and the budget is held until `approval.expiresAt`. Poll `GET /v1/purchase-requests/{purchaseRequestId}` for what they decided.
Requires an API key bound to an agent, or an OAuth connection granted `purchase:request`. An organization-scoped key names no identity to attribute a purchase to and is refused.
Credential: An API key bound to an agent. OAuth connections need `purchase:request`.
Parameters
| Name | In | Description |
|---|---|---|
Idempotency-Key | header | Makes a retry safe. The same key with the same body replays the original decision and reserves no further budget, answering 200 rather than 201. The same key with a different body is refused with 409 `idempotency_key_reuse`. This is the only operation that accepts one. |
Request body
| Field | Type | Description |
|---|---|---|
amountMinorrequired | string | integer | A whole number of ISO 4217 minor units. Send it as a string: JSON numbers lose precision above 2^53, and this is money. It is always returned as a string. |
category | string | null | |
currency | string | null | Defaults to the organisation's own currency when omitted. |
merchantrequired | string | Who the money would go to, written as a person would name them. |
merchantDomain | string | null | Recorded as evidence. Policy is not matched on domains. |
metadata | object | null | Anything you want recorded alongside the purchase. No rule reads it. |
reasonrequired | string | Why the agent wants this purchase. Every purchase must say. |
Responses
| Status | Meaning |
|---|---|
200 | An idempotent replay. The stored decision, returned again; no new budget was reserved. `replayed` is true. |
201 | The purchase was evaluated and a decision recorded. |
400 | invalid_requestmalformed_json |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_bound_to_agentinsufficient_scope |
409 | idempotency_key_reuse |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
approvalrequired | object | Whether a person still has to answer, and by when. The budget hold behind an escalation is released when the approval lapses. |
expiresAtrequired | Timestamp | null | |
requiredrequired | boolean | |
decisionrequired | Decision | |
engineVersionrequired | string | |
evaluatedAtrequired | Timestamp | |
explanationrequired | string | The sentence a person reads. Not a stable contract. |
idrequired | UUID | |
outcomerequired | APPROVED | REJECTED | REQUIRES_APPROVAL | Branch on this, never on the HTTP status code. |
reasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
policyVersionsrequired | object[] | |
applicabilityReasonrequired | string | |
policyVersionIdrequired | UUID | |
purchaseRequestrequired | PurchaseRequestSummary | |
amountMinorrequired | MinorAmount | |
currencyrequired | Currency | |
idrequired | UUID | |
statusrequired | EVALUATING | PENDING_APPROVAL | APPROVED | REJECTED | EXPIRED | CANCELLED | |
replayedrequired | boolean | True when this is the stored answer to an earlier identical request. The decision is real and binding either way; what differs is that no new budget was reserved. |
ruleEvaluationsrequired | RuleEvaluation[] | |
currency | Currency | null | |
headroomMinor | MinorAmount | null | |
limitMinor | MinorAmount | null | |
message | string | null | |
observedMinor | MinorAmount | null | |
outcomerequired | string | |
policyMerchantEntryId | UUID | null | |
policyRuleId | UUID | null | |
policyVersionId | UUID | null | |
reasonCode | string | null | |
rolerequired | string | |
rulerequired | string | The rule type as it was written into the policy version, snapshotted at evaluation time. |
Read back a purchase you submitted#
GET/v1/purchase-requests/{purchaseRequestId}
API key bound to an agentOAuth: purchase:read
What happened to a purchase, including the decision in force and the approval if one was opened. This is how you find out what a person decided after a `REQUIRES_APPROVAL`.
A purchase is readable by the credential that submitted it and by nothing else. Another agent's, another connection's, another tenant's and a malformed id are all the same 404.
Reads nothing into the audit trail and re-evaluates nothing.
Credential: An API key bound to an agent. OAuth connections need `purchase:read`.
Parameters
| Name | In | Description |
|---|---|---|
purchaseRequestIdrequired | path | The purchase request to read. |
Responses
| Status | Meaning |
|---|---|
200 | The purchase, as its submitter may see it. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_bound_to_agentinsufficient_scope |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
purchaserequired | PurchaseReadback | |
agentrequired | object | |
idrequired | UUID | |
namerequired | string | |
statusrequired | ENABLED | DISABLED | ARCHIVED | |
amountMinorrequired | MinorAmount | |
approval | object | null | |
commentrequired | string | null | |
escalationReasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
expiresAtrequired | Timestamp | |
isPendingrequired | boolean | |
resolvedAtrequired | Timestamp | null | |
resolvedByrequired | object | null | |
staterequired | PENDING | APPROVED | REJECTED | EXPIRED | CANCELLED | |
category | string | null | |
currencyrequired | Currency | |
decision | object | null | |
engineVersionrequired | string | |
evaluatedAtrequired | Timestamp | |
explanationrequired | string | |
kindrequired | string | INITIAL, or APPROVAL_REEVALUATION once a person answered. |
outcomerequired | APPROVED | REJECTED | REQUIRES_APPROVAL | |
reasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
idrequired | UUID | |
initiatedBy | object | null | |
namerequired | string | |
merchantrequired | string | |
merchantDomain | string | null | |
origin | DASHBOARD | API | MCP | null | |
reasonrequired | string | |
requestedAtrequired | Timestamp | |
resolvedAt | Timestamp | null | |
statusrequired | EVALUATING | PENDING_APPROVAL | APPROVED | REJECTED | EXPIRED | CANCELLED | |
submittedVia | object | object | null |
Simulations#
Ask what would happen, without anything happening. Simulations create no records and consume no budget.
Ask what would happen right now#
POST/v1/simulations/current-policies
API key of either kindOAuth: purchase:check
Runs the same evaluation `POST /v1/purchase-requests` would run, against the policies currently in force, and changes nothing: no purchase request, no decision, no budget reservation, no audit evidence, and no budget consumed.
This is how an agent finds out what it may spend before committing to asking.
Both API-key kinds reach it, and they behave differently: - an organization-scoped key must send `agentId` - an agent-bound key must not, and simulates as itself - an OAuth connection must not, and simulates as its agent
Credential: An API key of either kind. OAuth connections need `purchase:check`.
Request body
| Field | Type | Description |
|---|---|---|
agentId | UUID | null | Required for an organization-scoped key. Must be omitted by an agent-bound key or an OAuth connection, which simulate as themselves — a credential choosing which agent it asks about would be choosing its own authority. |
amountMinorrequired | string | integer | A whole number of ISO 4217 minor units. Send it as a string: JSON numbers lose precision above 2^53, and this is money. It is always returned as a string. |
category | string | null | |
currency | string | null | Defaults to the organisation's own currency when omitted. |
merchantrequired | string | Who the money would go to, written as a person would name them. |
merchantDomain | string | null | Recorded as evidence. Policy is not matched on domains. |
reasonrequired | string | Why the agent wants this purchase. Every purchase must say. |
Responses
| Status | Meaning |
|---|---|
200 | What would have happened. Nothing was changed. |
400 | invalid_requestmalformed_json |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedinsufficient_scope |
404 | not_found |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
agentrequired | object | null | |
id | UUID | |
name | string | |
status | ENABLED | DISABLED | ARCHIVED | |
budgetsrequired | SimulatedBudget[] | |
budgetCounterId | UUID | null | |
currency | Currency | |
current | object | |
committedMinor | MinorAmount | |
heldMinor | MinorAmount | |
openingBalanceMinor | MinorAmount | |
reservedMinor | MinorAmount | |
limits | object[] | |
effect | BLOCK | REQUIRE_APPROVAL | |
limitMinor | MinorAmount | |
policyRuleId | UUID | |
remainingAfterMinor | MinorAmount | Negative reads as "over by". |
remainingBeforeMinor | MinorAmount | |
wouldExceed | boolean | |
periodEnd | Timestamp | |
periodKey | string | |
periodKind | string | |
periodStart | Timestamp | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
simulated | object | |
projectedMinor | MinorAmount | |
requestedMinor | MinorAmount | |
wouldExceed | boolean | |
policiesConsideredrequired | object[] | |
applicabilityReason | string | |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyStatus | ACTIVE | DISABLED | ARCHIVED | |
policyVersionId | UUID | |
scopeType | ORGANIZATION | AGENT | |
versionNumber | integer | |
versionStatus | DRAFT | ACTIVE | ARCHIVED | |
policiesExcludedByCurrencyScoperequired | object[] | Candidates that govern this agent but declared a currency scope excluding this purchase. They changed nothing, and omitting them would make a NO_APPLICABLE_POLICY answer impossible to act on. |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
requestCurrency | Currency | |
versionNumber | integer | |
purchaserequired | object | |
amountMinor | MinorAmount | |
category | string | null | |
currency | Currency | |
currencyExponent | integer | |
merchant | string | |
merchantDomain | string | null | |
merchantKey | string | |
ruleEvaluationsrequired | RuleEvaluation[] | |
currency | Currency | null | |
headroomMinor | MinorAmount | null | |
limitMinor | MinorAmount | null | |
message | string | null | |
observedMinor | MinorAmount | null | |
outcomerequired | string | |
policyMerchantEntryId | UUID | null | |
policyRuleId | UUID | null | |
policyVersionId | UUID | null | |
reasonCode | string | null | |
rolerequired | string | |
rulerequired | string | The rule type as it was written into the policy version, snapshotted at evaluation time. |
policyId | UUID | null | |
policyName | string | null | |
simulatedDecisionrequired | object | |
explanationrequired | string | |
outcomerequired | APPROVED | REJECTED | REQUIRES_APPROVAL | |
reasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
wouldRequireApprovalrequired | boolean | |
simulationrequired | object | |
disclaimerrequired | string | |
engineVersionrequired | string | |
isSimulationrequired | true | Always true. The field exists so its absence is a signal. |
moderequired | string | |
simulatedAtrequired | Timestamp |
Ask what a draft policy version would decide#
POST/v1/simulations/draft-policy
Organisation-scoped API keyNo OAuth caller admitted
Evaluates a purchase against one `DRAFT` policy version as it is currently written — the question you ask before activating it.
Organization-scoped API keys only. An agent may not read or simulate the guardrails being drafted for it, and no OAuth scope reaches this.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Request body
| Field | Type | Description |
|---|---|---|
amountMinorrequired | string | integer | A whole number of ISO 4217 minor units. Send it as a string: JSON numbers lose precision above 2^53, and this is money. It is always returned as a string. |
category | string | null | |
currency | string | null | Defaults to the organisation's own currency when omitted. |
merchantrequired | string | Who the money would go to, written as a person would name them. |
merchantDomain | string | null | Recorded as evidence. Policy is not matched on domains. |
policyVersionIdrequired | UUID | The draft version to evaluate. Name the version, not the policy. |
reasonrequired | string | Why the agent wants this purchase. Every purchase must say. |
Responses
| Status | Meaning |
|---|---|
200 | What that draft would have decided. |
400 | invalid_requestmalformed_json |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
agentrequired | object | null | |
id | UUID | |
name | string | |
status | ENABLED | DISABLED | ARCHIVED | |
budgetsrequired | SimulatedBudget[] | |
budgetCounterId | UUID | null | |
currency | Currency | |
current | object | |
committedMinor | MinorAmount | |
heldMinor | MinorAmount | |
openingBalanceMinor | MinorAmount | |
reservedMinor | MinorAmount | |
limits | object[] | |
effect | BLOCK | REQUIRE_APPROVAL | |
limitMinor | MinorAmount | |
policyRuleId | UUID | |
remainingAfterMinor | MinorAmount | Negative reads as "over by". |
remainingBeforeMinor | MinorAmount | |
wouldExceed | boolean | |
periodEnd | Timestamp | |
periodKey | string | |
periodKind | string | |
periodStart | Timestamp | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
simulated | object | |
projectedMinor | MinorAmount | |
requestedMinor | MinorAmount | |
wouldExceed | boolean | |
policiesConsideredrequired | object[] | |
applicabilityReason | string | |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyStatus | ACTIVE | DISABLED | ARCHIVED | |
policyVersionId | UUID | |
scopeType | ORGANIZATION | AGENT | |
versionNumber | integer | |
versionStatus | DRAFT | ACTIVE | ARCHIVED | |
policiesExcludedByCurrencyScoperequired | object[] | Candidates that govern this agent but declared a currency scope excluding this purchase. They changed nothing, and omitting them would make a NO_APPLICABLE_POLICY answer impossible to act on. |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
requestCurrency | Currency | |
versionNumber | integer | |
purchaserequired | object | |
amountMinor | MinorAmount | |
category | string | null | |
currency | Currency | |
currencyExponent | integer | |
merchant | string | |
merchantDomain | string | null | |
merchantKey | string | |
ruleEvaluationsrequired | RuleEvaluation[] | |
currency | Currency | null | |
headroomMinor | MinorAmount | null | |
limitMinor | MinorAmount | null | |
message | string | null | |
observedMinor | MinorAmount | null | |
outcomerequired | string | |
policyMerchantEntryId | UUID | null | |
policyRuleId | UUID | null | |
policyVersionId | UUID | null | |
reasonCode | string | null | |
rolerequired | string | |
rulerequired | string | The rule type as it was written into the policy version, snapshotted at evaluation time. |
policyId | UUID | null | |
policyName | string | null | |
simulatedDecisionrequired | object | |
explanationrequired | string | |
outcomerequired | APPROVED | REJECTED | REQUIRES_APPROVAL | |
reasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
wouldRequireApprovalrequired | boolean | |
simulationrequired | object | |
disclaimerrequired | string | |
engineVersionrequired | string | |
isSimulationrequired | true | Always true. The field exists so its absence is a signal. |
moderequired | string | |
simulatedAtrequired | Timestamp |
Ask what a specific policy version would decide#
POST/v1/simulations/policy-version
Organisation-scoped API keyNo OAuth caller admitted
Evaluates a purchase against one `ACTIVE` or `ARCHIVED` policy version exactly as it was frozen — the question you ask about a decision that was already made, or about a version you are thinking of reinstating.
Organization-scoped API keys only.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Request body
| Field | Type | Description |
|---|---|---|
amountMinorrequired | string | integer | A whole number of ISO 4217 minor units. Send it as a string: JSON numbers lose precision above 2^53, and this is money. It is always returned as a string. |
category | string | null | |
currency | string | null | Defaults to the organisation's own currency when omitted. |
merchantrequired | string | Who the money would go to, written as a person would name them. |
merchantDomain | string | null | Recorded as evidence. Policy is not matched on domains. |
policyVersionIdrequired | UUID | The active or archived version to evaluate. |
reasonrequired | string | Why the agent wants this purchase. Every purchase must say. |
Responses
| Status | Meaning |
|---|---|
200 | What that version would have decided. |
400 | invalid_requestmalformed_json |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
agentrequired | object | null | |
id | UUID | |
name | string | |
status | ENABLED | DISABLED | ARCHIVED | |
budgetsrequired | SimulatedBudget[] | |
budgetCounterId | UUID | null | |
currency | Currency | |
current | object | |
committedMinor | MinorAmount | |
heldMinor | MinorAmount | |
openingBalanceMinor | MinorAmount | |
reservedMinor | MinorAmount | |
limits | object[] | |
effect | BLOCK | REQUIRE_APPROVAL | |
limitMinor | MinorAmount | |
policyRuleId | UUID | |
remainingAfterMinor | MinorAmount | Negative reads as "over by". |
remainingBeforeMinor | MinorAmount | |
wouldExceed | boolean | |
periodEnd | Timestamp | |
periodKey | string | |
periodKind | string | |
periodStart | Timestamp | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
simulated | object | |
projectedMinor | MinorAmount | |
requestedMinor | MinorAmount | |
wouldExceed | boolean | |
policiesConsideredrequired | object[] | |
applicabilityReason | string | |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyStatus | ACTIVE | DISABLED | ARCHIVED | |
policyVersionId | UUID | |
scopeType | ORGANIZATION | AGENT | |
versionNumber | integer | |
versionStatus | DRAFT | ACTIVE | ARCHIVED | |
policiesExcludedByCurrencyScoperequired | object[] | Candidates that govern this agent but declared a currency scope excluding this purchase. They changed nothing, and omitting them would make a NO_APPLICABLE_POLICY answer impossible to act on. |
currencies | Currency[] | |
currencyScope | string | null | |
policyId | UUID | |
policyName | string | |
policyVersionId | UUID | |
requestCurrency | Currency | |
versionNumber | integer | |
purchaserequired | object | |
amountMinor | MinorAmount | |
category | string | null | |
currency | Currency | |
currencyExponent | integer | |
merchant | string | |
merchantDomain | string | null | |
merchantKey | string | |
ruleEvaluationsrequired | RuleEvaluation[] | |
currency | Currency | null | |
headroomMinor | MinorAmount | null | |
limitMinor | MinorAmount | null | |
message | string | null | |
observedMinor | MinorAmount | null | |
outcomerequired | string | |
policyMerchantEntryId | UUID | null | |
policyRuleId | UUID | null | |
policyVersionId | UUID | null | |
reasonCode | string | null | |
rolerequired | string | |
rulerequired | string | The rule type as it was written into the policy version, snapshotted at evaluation time. |
policyId | UUID | null | |
policyName | string | null | |
simulatedDecisionrequired | object | |
explanationrequired | string | |
outcomerequired | APPROVED | REJECTED | REQUIRES_APPROVAL | |
reasonCodesrequired | WITHIN_POLICY | MERCHANT_BLOCKED | MERCHANT_NOT_ALLOW_LISTED | AMOUNT_EXCEEDS_MAX | APPROVAL_THRESHOLD_EXCEEDED | DAILY_LIMIT_EXCEEDED | MONTHLY_LIMIT_EXCEEDED | AGENT_DISABLED | NO_APPLICABLE_POLICY | CURRENCY_NOT_COVERED | POLICY_CHANGED_SINCE_ESCALATION[] | |
wouldRequireApprovalrequired | boolean | |
simulationrequired | object | |
disclaimerrequired | string | |
engineVersionrequired | string | |
isSimulationrequired | true | Always true. The field exists so its absence is a signal. |
moderequired | string | |
simulatedAtrequired | Timestamp |
Policies#
Read the spending rules in force. Policies are authored by a person in the dashboard, never by an API credential.
List spending policies#
GET/v1/policies
Organisation-scoped API keyNo OAuth caller admitted
Your organisation's spending policies, newest first.
Organization-scoped API keys only. An agent may not read the policies it is judged against — it learns what it may spend by asking `POST /v1/simulations/current-policies` instead. No OAuth scope reaches this.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
status | query | Return only policies in this state. |
search | query | Free-text match on the policy name. Trimmed, capped at 200 characters, and treated as absent when empty. |
page | query | 1-based page number. A page beyond the end returns an empty page rather than a 404, because a filtered set shrinking under a paging UI is normal. |
pageSize | query | Rows per page. Clamped to 100 rather than refused: a caller asking for everything gets the maximum. A malformed value is refused, because it means the client believes something untrue. |
Responses
| Status | Meaning |
|---|---|
200 | A page of policies, and where it sits in the collection. |
400 | invalid_request |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
paginationrequired | Pagination | |
pagerequired | integer | |
pageSizerequired | integer | |
totalrequired | integer | |
totalPagesrequired | integer | At least 1 even when nothing matched, so a client can render "1 of 1" for an empty result and never divide by zero. |
policiesrequired | PolicySummary[] | |
activeVersionrequired | PolicyVersionSummary | null | |
activatedAt | Timestamp | null | |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
currenciesrequired | Currency[] | The currencies this version covers. A purchase in any other currency is refused with CURRENCY_NOT_COVERED: Veriticity performs no currency conversion. |
currencyScope | string | null | |
idrequired | UUID | |
merchantCountrequired | integer | How many merchant entries this version carries. |
ruleCountrequired | integer | How many rules this version carries, without fetching them. |
statusrequired | DRAFT | ACTIVE | ARCHIVED | |
versionNumberrequired | integer | Allocated by Veriticity. Never chosen by a caller. |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
description | string | null | |
idrequired | UUID | |
inForcerequired | boolean | Whether the Trust Engine is actually reading this policy right now. "Has a live version" and "is being enforced" are different facts: a DISABLED policy keeps its ACTIVE version but the engine skips it. |
namerequired | string | |
scopeTyperequired | ORGANIZATION | AGENT | |
statusrequired | ACTIVE | DISABLED | ARCHIVED | |
updatedAtrequired | Timestamp |
Read one policy#
GET/v1/policies/{policyId}
Organisation-scoped API keyNo OAuth caller admitted
One policy with its version history and the agents it governs.
Organization-scoped API keys only. An agent may not read the policies it is judged against — it learns what it may spend by asking `POST /v1/simulations/current-policies` instead. No OAuth scope reaches this.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
policyIdrequired | path | The policy to read. |
Responses
| Status | Meaning |
|---|---|
200 | The policy. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
policyrequired | PolicySummary | |
activeVersionrequired | PolicyVersionSummary | null | |
activatedAt | Timestamp | null | |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
currenciesrequired | Currency[] | The currencies this version covers. A purchase in any other currency is refused with CURRENCY_NOT_COVERED: Veriticity performs no currency conversion. |
currencyScope | string | null | |
idrequired | UUID | |
merchantCountrequired | integer | How many merchant entries this version carries. |
ruleCountrequired | integer | How many rules this version carries, without fetching them. |
statusrequired | DRAFT | ACTIVE | ARCHIVED | |
versionNumberrequired | integer | Allocated by Veriticity. Never chosen by a caller. |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
description | string | null | |
idrequired | UUID | |
inForcerequired | boolean | Whether the Trust Engine is actually reading this policy right now. "Has a live version" and "is being enforced" are different facts: a DISABLED policy keeps its ACTIVE version but the engine skips it. |
namerequired | string | |
scopeTyperequired | ORGANIZATION | AGENT | |
statusrequired | ACTIVE | DISABLED | ARCHIVED | |
updatedAtrequired | Timestamp | |
agentIds | UUID[] | |
versions | PolicyVersionSummary[] | |
activatedAt | Timestamp | null | |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
currenciesrequired | Currency[] | The currencies this version covers. A purchase in any other currency is refused with CURRENCY_NOT_COVERED: Veriticity performs no currency conversion. |
currencyScope | string | null | |
idrequired | UUID | |
merchantCountrequired | integer | How many merchant entries this version carries. |
ruleCountrequired | integer | How many rules this version carries, without fetching them. |
statusrequired | DRAFT | ACTIVE | ARCHIVED | |
versionNumberrequired | integer | Allocated by Veriticity. Never chosen by a caller. |
Read one policy version#
GET/v1/policies/{policyId}/versions/{versionId}
Organisation-scoped API keyNo OAuth caller admitted
A policy version in full, including its rules and merchant lists, exactly as the Trust Engine reads it.
Organization-scoped API keys only. An agent may not read the policies it is judged against — it learns what it may spend by asking `POST /v1/simulations/current-policies` instead. No OAuth scope reaches this.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
policyIdrequired | path | The policy the version belongs to. |
versionIdrequired | path | The version to read. |
Responses
| Status | Meaning |
|---|---|
200 | The version. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
versionrequired | PolicyVersion | |
activatedAt | Timestamp | null | |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
currenciesrequired | Currency[] | The currencies this version covers. A purchase in any other currency is refused with CURRENCY_NOT_COVERED: Veriticity performs no currency conversion. |
currencyScope | string | null | |
idrequired | UUID | |
merchantCountrequired | integer | How many merchant entries this version carries. |
ruleCountrequired | integer | How many rules this version carries, without fetching them. |
statusrequired | DRAFT | ACTIVE | ARCHIVED | |
versionNumberrequired | integer | Allocated by Veriticity. Never chosen by a caller. |
merchantsrequired | PolicyMerchantEntry[] | |
idrequired | UUID | |
labelrequired | string | The merchant as a person wrote it. |
listTyperequired | ALLOW | BLOCK | |
merchantKeyrequired | string | The normalised form the engine matches on. Veriticity derives it from the label; it is never supplied directly. |
policyIdrequired | UUID | |
rulesrequired | PolicyRule[] | |
amountMinorrequired | MinorAmount | |
currency | Currency | null | |
effectrequired | BLOCK | REQUIRE_APPROVAL | BLOCK refuses outright. REQUIRE_APPROVAL holds the budget and waits for a person. |
idrequired | UUID | |
periodKindrequired | PER_REQUEST | DAY | MONTH | PER_REQUEST for a single-purchase cap; DAY or MONTH for a window, resolved against the organisation's own timezone. |
ruleTyperequired | AMOUNT_LIMIT | SPEND_LIMIT | AMOUNT_LIMIT caps a single purchase. SPEND_LIMIT caps the total across a period. |
Check whether a version could be activated#
POST/v1/policies/{policyId}/versions/{versionId}/validate
Organisation-scoped API keyNo OAuth caller admitted
Runs activation's own check as a dry run, and changes nothing.
Answers 200 even when the draft is unacceptable — `valid` carries the verdict and `issues` carries the reasons. A 4xx would mean the question could not be answered, and it was.
`budgetImpact` is empty in the ordinary case. A non-empty one means activating this version would start a limit from a total that already includes spending nobody reserved against.
This is a `POST` that writes nothing, and it is the only policy operation on `/v1` that is not a plain read.
Organization-scoped API keys only. An agent may not read the policies it is judged against — it learns what it may spend by asking `POST /v1/simulations/current-policies` instead. No OAuth scope reaches this.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
policyIdrequired | path | The policy the version belongs to. |
versionIdrequired | path | The version to check. |
Responses
| Status | Meaning |
|---|---|
200 | The verdict. `valid` is false when the version could not be activated; this is still a 200. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
budgetImpactrequired | object[] | |
attributableMinor | MinorAmount | |
currency | Currency | |
limits | object[] | |
effect | string | |
limitMinor | MinorAmount | |
remainingMinor | MinorAmount | |
openingBalanceMinor | MinorAmount | |
periodKey | string | |
periodKind | string | |
reservedMinor | MinorAmount | |
issuesrequired | PolicyIssue[] | |
coderequired | string | |
messagerequired | string | |
path | string | |
validrequired | boolean | |
versionrequired | PolicyVersion | |
activatedAt | Timestamp | null | |
archivedAt | Timestamp | null | |
createdAtrequired | Timestamp | |
currenciesrequired | Currency[] | The currencies this version covers. A purchase in any other currency is refused with CURRENCY_NOT_COVERED: Veriticity performs no currency conversion. |
currencyScope | string | null | |
idrequired | UUID | |
merchantCountrequired | integer | How many merchant entries this version carries. |
ruleCountrequired | integer | How many rules this version carries, without fetching them. |
statusrequired | DRAFT | ACTIVE | ARCHIVED | |
versionNumberrequired | integer | Allocated by Veriticity. Never chosen by a caller. |
merchantsrequired | PolicyMerchantEntry[] | |
idrequired | UUID | |
labelrequired | string | The merchant as a person wrote it. |
listTyperequired | ALLOW | BLOCK | |
merchantKeyrequired | string | The normalised form the engine matches on. Veriticity derives it from the label; it is never supplied directly. |
policyIdrequired | UUID | |
rulesrequired | PolicyRule[] | |
amountMinorrequired | MinorAmount | |
currency | Currency | null | |
effectrequired | BLOCK | REQUIRE_APPROVAL | BLOCK refuses outright. REQUIRE_APPROVAL holds the budget and waits for a person. |
idrequired | UUID | |
periodKindrequired | PER_REQUEST | DAY | MONTH | PER_REQUEST for a single-purchase cap; DAY or MONTH for a window, resolved against the organisation's own timezone. |
ruleTyperequired | AMOUNT_LIMIT | SPEND_LIMIT | AMOUNT_LIMIT caps a single purchase. SPEND_LIMIT caps the total across a period. |
warningsrequired | PolicyIssue[] | |
coderequired | string | |
messagerequired | string | |
path | string |
Agents#
The identities that spend.
List this organisation's agents#
GET/v1/agents
Organisation-scoped API keyNo OAuth caller admitted
The agents in your organisation, with their status.
Organization-scoped API keys only. An agent-bound credential is refused: a roster of an organisation's other agents is a map of the identities a compromised agent might try to impersonate.
Read-only. Agents are created in the dashboard.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Responses
| Status | Meaning |
|---|---|
200 | Every agent in the organisation. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
agentsrequired | object[] | |
createdAtrequired | Timestamp | |
description | string | null | |
idrequired | UUID | |
namerequired | string | |
statusrequired | ENABLED | DISABLED | ARCHIVED | |
updatedAtrequired | Timestamp |
Webhooks#
Receive events when purchases are decided, budgets move, or connections change.
List webhook endpoints#
GET/v1/webhooks
Organisation-scoped API keyNo OAuth caller admitted
Every webhook endpoint on the account, including revoked ones — the delivery history against a revoked endpoint is the answer to "where were our events going in March".
Never carries a signing secret.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Responses
| Status | Meaning |
|---|---|
200 | The account's endpoints. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
itemsrequired | WebhookEndpoint[] | |
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string |
Create a webhook endpoint#
POST/v1/webhooks
Organisation-scoped API keyNo OAuth caller admitted
Registers a URL to receive events, and returns the signing secret.
The secret is shown once. Veriticity cannot show it again — store it before you close the response. If you lose it, rotate.
The URL must be HTTPS on port 443, must be a hostname rather than an IP literal, must carry no userinfo, and must resolve to a publicly routable address. This is checked now and again before every delivery. A URL that fails answers `webhook_url_not_permitted`.
New endpoints are created on the current webhook API version.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Request body
| Field | Type | Description |
|---|---|---|
description | string | null | For your own reference. Never sent anywhere. |
subscribedTypesrequired | WebhookEventType[] | Choose at least one event to receive. |
urlrequired | string |
Responses
| Status | Meaning |
|---|---|
201 | Created. **Carries the signing secret, once.** |
400 | invalid_requestmalformed_jsonwebhook_url_not_permitted |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
409 | webhook_limit_reached |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
503 | webhook_not_configured |
Response body
| Field | Type | Description |
|---|---|---|
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string | |
secretrequired | string | The signing secret, shown exactly once. Veriticity cannot show it again. Store it before you close the response. |
Read a webhook endpoint#
GET/v1/webhooks/{webhookId}
Organisation-scoped API keyNo OAuth caller admitted
One endpoint, including its operational health. `consecutiveFailures` is the number that tells you a receiver has been refusing events since Tuesday; any success resets it.
Never carries a signing secret.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint to read. |
Responses
| Status | Meaning |
|---|---|
200 | The endpoint. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string |
Update a webhook endpoint#
PATCH/v1/webhooks/{webhookId}
Organisation-scoped API keyNo OAuth caller admitted
Changes the URL, description, subscriptions, status or API version. Send at least one field.
Omitting `description` leaves it alone; sending `null` clears it.
`status` accepts `ACTIVE` and `DISABLED` only. Revoking destroys key material and cannot be undone, so it is its own verb: `DELETE /v1/webhooks/{webhookId}`.
The secret is never changed here — use rotate-secret.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint to update. |
Request body
| Field | Type | Description |
|---|---|---|
apiVersion | string | |
description | string | null | |
status | ACTIVE | DISABLED | |
subscribedTypes | WebhookEventType[] | |
url | string |
Responses
| Status | Meaning |
|---|---|
200 | The updated endpoint. |
400 | invalid_requestmalformed_jsonwebhook_url_not_permitted |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
409 | webhook_endpoint_revoked |
413 | payload_too_large |
415 | unsupported_media_type |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string |
Revoke a webhook endpoint#
DELETE/v1/webhooks/{webhookId}
Organisation-scoped API keyNo OAuth caller admitted
Stops delivery permanently and destroys the endpoint's signing secrets. Revocation is terminal — it cannot be undone, and a revoked endpoint cannot be re-enabled. Create a new one instead.
Returns the endpoint in its revoked state rather than `204`: the row is not gone, it still appears in listings, and its delivery history survives.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint to revoke. |
Responses
| Status | Meaning |
|---|---|
200 | The endpoint, now revoked. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
409 | webhook_endpoint_revoked |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string |
Read an endpoint's delivery history#
GET/v1/webhooks/{webhookId}/deliveries
Organisation-scoped API keyNo OAuth caller admitted
Recent delivery attempts, newest first — what was sent, what your server answered, and when the next attempt is due.
`hasMore` rather than a total: what a debugging session wants is "is there anything older", and counting a table that only grows costs a scan to answer a question nobody asks.
This is a third pagination model, distinct from the page/pageSize used by `GET /v1/policies`.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint whose deliveries these are. |
limit | query | How many attempts to return. |
Responses
| Status | Meaning |
|---|---|
200 | A page of delivery attempts. |
400 | invalid_request |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
500 | internal_error |
Response body
| Field | Type | Description |
|---|---|---|
hasMorerequired | boolean | |
itemsrequired | WebhookDelivery[] | |
attemptCountrequired | integer | |
createdAtrequired | Timestamp | |
deliveredAt | Timestamp | null | |
eventIdrequired | UUID | The logical event, and your deduplication key. Identical to the Veriticity-Webhook-Id header, and identical at every endpoint subscribed to this event. |
eventTyperequired | DeliverableWebhookEventType | |
idrequired | UUID | This delivery: one endpoint's attempt stream for one event. |
lastAttemptAt | Timestamp | null | |
lastDurationMs | integer | null | |
lastErrorClass | string | null | |
lastResponseSnippet | string | null | |
lastStatusCode | integer | null | |
nextAttemptAt | Timestamp | null | |
occurredAtrequired | Timestamp | When it happened in the domain, not when it was sent. |
statusrequired | string |
Rotate an endpoint's signing secret#
POST/v1/webhooks/{webhookId}/rotate-secret
Organisation-scoped API keyNo OAuth caller admitted
Issues a new signing secret and returns it once.
By default the old secret keeps signing for 24 hours alongside the new one, so every delivery in that window carries two signatures and a receiver verifying with either succeeds. That is what lets you deploy the new secret without coordinating a cutover.
Send `{"retireImmediately": true}` to kill the old secret at once, which is what a leak calls for.
The body is optional: an absent body means the defaults. This is the only operation on the API that does not require a JSON body when it takes one.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint to rotate. |
Request body (optional)
| Field | Type | Description |
|---|---|---|
retireImmediately | boolean | Retire the old secret now instead of after 24 hours. Deliveries signed with it stop verifying immediately. |
Responses
| Status | Meaning |
|---|---|
200 | Rotated. **Carries the new signing secret, once.** |
400 | invalid_requestmalformed_json |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
409 | webhook_endpoint_revoked |
500 | internal_error |
503 | webhook_not_configured |
Response body
| Field | Type | Description |
|---|---|---|
apiVersionrequired | string | The webhook contract version this endpoint receives. Pinned when the endpoint is created and changed only by PATCH. |
consecutiveFailuresrequired | integer | Veriticity does not disable a failing endpoint, so this is the number that tells you a receiver has been refusing events. Any success resets it. |
createdAtrequired | Timestamp | |
description | string | null | |
disabledAt | Timestamp | null | |
idrequired | UUID | |
lastFailureAt | Timestamp | null | |
lastSuccessAt | Timestamp | null | |
revokedAt | Timestamp | null | |
statusrequired | ACTIVE | DISABLED | REVOKED | |
subscribedTypesrequired | WebhookEventType[] | |
urlrequired | string | |
secretrequired | string | The signing secret, shown exactly once. Veriticity cannot show it again. Store it before you close the response. |
Send a test event#
POST/v1/webhooks/{webhookId}/test
Organisation-scoped API keyNo OAuth caller admitted
Queues a real `webhook.test` event through the real pipeline: signed with your real secret, recorded as a real delivery, retried on the real schedule. There is no verification handshake — this is better, because it proves the thing you actually care about on every request rather than once.
Answers 202, not 200: the event is queued rather than delivered. Use the returned ids to find it in the delivery history.
Delivered regardless of what the endpoint subscribes to.
Organization-scoped API keys only. An agent-bound credential must not be able to decide where the whole account's events go, and no OAuth scope reaches this at all — admitting a connection would let an AI assistant point your event stream at a URL of its own choosing.
Credential: An organization-scoped API key. No OAuth scope reaches this operation.
Parameters
| Name | In | Description |
|---|---|---|
webhookIdrequired | path | The endpoint to send a test to. |
Responses
| Status | Meaning |
|---|---|
202 | Queued. Look in the delivery history for what happened. |
401 | unauthorizedinvalid_api_keyapi_key_revokedapi_key_expiredwrong_environmentinvalid_token |
403 | connection_unauthorizedorganization_suspendedapi_key_not_organization_scopedoauth_not_permitted |
404 | not_found |
409 | webhook_endpoint_revoked |
500 | internal_error |
503 | webhook_not_configured |
Response body
| Field | Type | Description |
|---|---|---|
deliveryIdrequired | UUID | |
eventIdrequired | UUID | |
statusrequired | "queued" |
Using the OpenAPI document#
The document is OpenAPI 3.1, so its schemas are JSON Schema 2020-12 and can be used directly by a validator. It is generated from the application’s own constants and regenerated on every build, so it cannot describe an endpoint that does not exist or omit an error code the API can return.