Veto/docs

Decisions API

Query, aggregate, and log validation decisions — the audit trail for every tool call.

Every tool call validated by Veto produces a decision record. These endpoints let you query, aggregate, and log decisions.

GET /v1/decisions

List decisions with pagination and filtering.

Headers

HeaderRequiredDescription
X-Veto-API-Key or AuthorizationYesAPI key or Bearer JWT

Query parameters

ParameterTypeDefaultDescription
limitnumber50Results per page (1–100)
offsetnumber0Skip this many results
projectIdstring (UUID)Filter by project
toolNamestringFilter by tool name
decision"allow" | "deny"Filter by decision
startDateISO 8601 dateFilter from this date
endDateISO 8601 dateFilter until this date

Response

{
  "data": [
    {
      "id": "d7f2a1b3-4c5e-6f78-9a0b-1c2d3e4f5a6b",
      "toolName": "transfer_funds",
      "arguments": { "amount": 500, "to": "vendor-123" },
      "context": { "session_id": "sess_abc123", "source": "client" },
      "decision": "allow",
      "mode": "deterministic",
      "reason": "All constraints passed",
      "failedArgument": null,
      "matchedException": null,
      "validations": null,
      "latencyMs": 2,
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ],
  "pagination": {
    "total": 1247,
    "limit": 50,
    "offset": 0,
    "hasMore": true
  }
}

GET /v1/decisions/stats

Aggregated decision statistics over a time period.

Headers

HeaderRequiredDescription
X-Veto-API-Key or AuthorizationYesAPI key or Bearer JWT

Query parameters

ParameterTypeDefaultDescription
daysnumber7Lookback period (1–90)
projectIdstring (UUID)Filter by project

Response

{
  "period": { "days": 7 },
  "summary": {
    "total": 1247,
    "allowed": 1180,
    "denied": 67,
    "deterministic": 1100,
    "llm": 147
  },
  "breakdown": [
    {
      "date": "2025-01-15",
      "allowed": 180,
      "denied": 12,
      "deterministic": 160,
      "llm": 32
    }
  ]
}

GET /v1/decisions/:id

Get a single decision by ID.

Headers

HeaderRequiredDescription
X-Veto-API-Key or AuthorizationYesAPI key or Bearer JWT

Path parameters

ParameterTypeDescription
idstring (UUID)Decision ID

Response

Same shape as individual items in the GET /v1/decisions data array.

Errors

StatusDescription
400Invalid UUID format
404Decision not found

POST /v1/decisions

Log a client-side validation decision. Keeps the dashboard audit trail complete when the SDK validates locally.

Called automatically by the SDK — you do not need to call this directly.

Headers

HeaderRequiredDescription
X-Veto-API-KeyYesAPI key scoped to a project
Content-TypeYesapplication/json

Body

{
  "tool_name": "transfer_funds",
  "arguments": { "amount": 500, "to": "vendor-123" },
  "decision": "allow",
  "reason": "All constraints passed",
  "mode": "deterministic",
  "latency_ms": 2,
  "source": "client",
  "context": {
    "session_id": "sess_abc123",
    "agent_id": "agent_1"
  }
}
FieldTypeRequiredDescription
tool_namestringYesName of the tool that was validated
argumentsobjectYesArguments that were validated
decision"allow" | "deny"YesValidation result
reasonstringNoExplanation of the decision
mode"deterministic"YesValidation mode used
latency_msnumberYesClient-side validation time in milliseconds
source"client"YesMust be "client"
contextobjectNoAdditional metadata (session_id, agent_id, etc.)

Response (201)

{
  "success": true,
  "id": "d7f2a1b3-4c5e-6f78-9a0b-1c2d3e4f5a6b"
}

Errors

StatusCodeDescription
400missing_projectAPI key must be scoped to a project

Rate limiting

120 requests per 60 seconds per API key.

SDK behavior

The SDK calls POST /v1/decisions automatically after every client-side deterministic validation. The call is fire-and-forget — errors are silently swallowed so they never block tool execution.

Both TypeScript and Python SDKs use the same behavior:

// TypeScript — called internally by tryLocalDeterministic()
client.logDecision({
  tool_name: 'send_email',
  arguments: { to: 'user@example.com' },
  decision: 'allow',
  mode: 'deterministic',
  latency_ms: 2,
  source: 'client',
});
# Python — called internally by try_local_deterministic()
client.log_decision({
    "tool_name": "send_email",
    "arguments": {"to": "user@example.com"},
    "decision": "allow",
    "mode": "deterministic",
    "latency_ms": 2,
    "source": "client",
})

Decisions logged via this endpoint appear in the dashboard alongside server-side decisions. They are marked with source: "client" in the context field.