Veto/docs

Approvals API

Endpoints for human-in-the-loop approval workflows — list, create, poll, and resolve approvals.

The approvals API powers Veto's human-in-the-loop (HITL) workflow. When a policy requires human review, an approval record is created and the SDK polls until a human resolves it.

HITL requires the Team plan or above.

GET /v1/approvals/pending

List all pending approvals for the current organization.

Headers

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

Response

{
  "data": [
    {
      "id": "apr_abc123def456",
      "toolName": "transfer_funds",
      "arguments": { "amount": 5000, "to": "vendor-123" },
      "status": "pending",
      "expiresAt": "2025-01-15T10:35:00Z",
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ]
}

POST /v1/approvals

Create an approval request manually. Useful for triggering HITL outside the normal validation flow.

Headers

HeaderRequiredDescription
X-Veto-API-Key or AuthorizationYesAPI key or Bearer JWT
Content-TypeYesapplication/json

Body

{
  "decisionId": "d7f2a1b3-4c5e-6f78-9a0b-1c2d3e4f5a6b",
  "toolName": "transfer_funds",
  "arguments": { "amount": 5000, "to": "vendor-123" },
  "expiresInSeconds": 3600
}
FieldTypeRequiredDescription
decisionIdstring (UUID)NoLink to an existing decision record
toolNamestringYesName of the tool awaiting approval
argumentsobjectNoTool call arguments for reviewer context
expiresInSecondsnumberNoTTL in seconds (60–86400, default: 3600)

Response (201)

{
  "id": "apr_abc123def456",
  "toolName": "transfer_funds",
  "status": "pending",
  "expiresAt": "2025-01-15T11:30:00Z",
  "createdAt": "2025-01-15T10:30:00Z"
}

GET /v1/approvals/:id

Poll an approval record. The SDK calls this automatically — you do not need to call it directly.

Headers

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

Path parameters

ParameterTypeDescription
idstringThe approval ID

Response

Pending

{
  "id": "apr_abc123def456",
  "status": "pending",
  "toolName": "transfer_funds",
  "arguments": { "amount": 5000, "to": "vendor-123" },
  "createdAt": "2025-01-15T10:30:00Z",
  "expiresAt": "2025-01-15T10:35:00Z"
}

Approved

{
  "id": "apr_abc123def456",
  "status": "approved",
  "toolName": "transfer_funds",
  "resolvedBy": "user@company.com",
  "resolvedAt": "2025-01-15T10:31:15Z"
}

Denied

{
  "id": "apr_abc123def456",
  "status": "denied",
  "toolName": "transfer_funds",
  "resolvedBy": "user@company.com",
  "resolvedAt": "2025-01-15T10:31:15Z"
}

Expired

{
  "id": "apr_abc123def456",
  "status": "expired",
  "toolName": "transfer_funds",
  "expiresAt": "2025-01-15T10:35:00Z"
}

Response fields

FieldTypeDescription
idstringApproval record ID
status"pending" | "approved" | "denied" | "expired"Current status
toolNamestringName of the tool awaiting approval
argumentsobject?Tool call arguments (included while pending)
resolvedBystring?Who resolved the approval
resolvedAtstring?ISO 8601 timestamp of resolution
createdAtstring?ISO 8601 timestamp of creation
expiresAtstring?ISO 8601 timestamp of expiration

POST /v1/approvals/:id/resolve

Resolve a pending approval — approve or deny.

Headers

HeaderRequiredDescription
X-Veto-API-Key or AuthorizationYesAPI key or Bearer JWT
Content-TypeYesapplication/json

Path parameters

ParameterTypeDescription
idstringThe approval ID to resolve

Body

{
  "action": "approve",
  "resolvedBy": "user@company.com"
}
FieldTypeRequiredDescription
action"approve" | "deny"YesResolution action
resolvedBystringYesIdentifier of the person resolving

Response

{
  "id": "apr_abc123def456",
  "status": "approved",
  "resolvedBy": "user@company.com",
  "resolvedAt": "2025-01-15T10:31:15Z"
}

Errors

StatusCodeDescription
404not_foundApproval ID does not exist
410expiredApproval has already expired
400invalid_statusApproval is already resolved

SDK behavior

The SDK handles polling automatically. The flow:

  1. Validate endpoint returns { decision: "require_approval", approval_id: "apr_..." }
  2. SDK fires the onApprovalRequired / on_approval_required hook
  3. SDK polls GET /v1/approvals/:id every 2 seconds (configurable)
  4. When status changes from pending, SDK returns the decision to the agent
  5. If the poll exceeds the timeout (default 5 minutes), SDK throws ApprovalTimeoutError
Agent calls tool


SDK validates with cloud ──── allow ──── Tool executes

       ├── deny ──── ToolCallDeniedError

       └── require_approval


       Fire onApprovalRequired hook


       Poll GET /v1/approvals/:id

       ┌──────┼──────┐
       │      │      │
  approved  denied  expired/timeout
       │      │      │
  Tool runs  Error  Error