This document describes the canonical first-party machine integration path for Approva.
Use this flow for:
- AI agents
- backend services
- CI/CD runners
- automation workers
- tool wrappers that need a human approval checkpoint before continuing
- authenticate with an organization API key
- create an approval request
- include a signed webhook callback
- enable
deliverCapabilityMode = exchange_token - wait for
approval_request.approved - exchange the one-time delivery token
- use the opaque capability token
- continue the protected action
This is the recommended continuation path in the current open-core runtime.
It is first-party, supported, and aligned with Approva's security model.
Approva intentionally does not allow later raw capability-token fetches by id.
That means a machine client should not expect to:
- poll for a request and later retrieve the raw capability token
- read the token back from the console
- fetch the token through another API after approval
Instead, Approva delivers a short-lived single-use exchange token on the approved webhook. The machine client exchanges that token once and receives the raw opaque capability token at that point.
- an Approva organization
- a machine API key in the format
approva_sk_... - an approval policy that requires approval for the action you want to gate
- a webhook receiver that can verify:
X-Approval-SignatureX-Approval-Timestamp
Machine clients authenticate with:
Authorization: Bearer approva_sk_...The API key resolves:
- organization
- key status
- key scopes
- optional service-account identity
Create the request with:
- your machine API key
- a callback webhook URL
deliverCapabilityMode = exchange_token
Example:
curl -X POST "http://localhost:4000/v1/approval-requests" \
-H "Authorization: Bearer approva_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-run-001" \
-d '{
"externalRequestId": "deploy-run-001",
"requestedBy": {
"system": "deploy-agent",
"actorId": "billing-api-release-runner"
},
"action": "deployment.execute",
"riskLevel": "high",
"resource": {
"type": "service",
"id": "billing-api"
},
"params": {
"environment": "production",
"version": "2026.03.16-demo",
"region": "eu-west-1"
},
"callback": {
"webhookUrl": "https://agent.example.com/webhooks/approva",
"deliverCapabilityMode": "exchange_token"
}
}'If the request becomes pending, Approva returns an approvalUrl for the human approver.
If the request is auto_approved, Approva may return a capability immediately and no webhook wait is required.
When a human approves the request, Approva sends a signed approval_request.approved webhook.
In exchange_token mode, the webhook payload includes:
approvalRequestIdcapabilityIdcapabilityExchangeTokencapabilityExchangeExpiresAt
Example payload:
{
"id": "3f01c902-3c06-4429-a6b5-96f2436fe8a8",
"approvalRequestId": "7e1c48b5-708d-402a-ae32-d5ca90b935ff",
"eventType": "approval_request.approved",
"occurredAt": "2026-03-16T13:45:21.000Z",
"payload": {
"approvalRequestId": "7e1c48b5-708d-402a-ae32-d5ca90b935ff",
"status": "approved",
"capabilityId": "91f8ef51-61cf-4f0a-80c0-8cfd5fe969df",
"capabilityExchangeToken": "cex_8Fsd9Kj3l2PQx0HnV7eTsU6cM4RbYaQ",
"capabilityExchangeExpiresAt": "2026-03-16T13:50:21.000Z"
}
}Your receiver should verify the signature before parsing or acting on the payload.
Exchange the webhook token immediately:
curl -X POST "http://localhost:4000/v1/capabilities/exchange" \
-H "Authorization: Bearer approva_sk_..." \
-H "Content-Type: application/json" \
-d '{
"exchangeToken": "cex_..."
}'Response:
{
"capabilityToken": "cap_8Fsd9Kj3l2PQx0HnV7eTsU6cM4RbYaQ",
"expiresAt": "2026-03-16T14:00:21.000Z",
"scope": {
"approvalRequestId": "7e1c48b5-708d-402a-ae32-d5ca90b935ff",
"action": "deployment.execute",
"resource": {
"type": "service",
"id": "billing-api"
}
}
}Important properties:
- exchange tokens are short-lived
- exchange tokens are single-use
- Approva stores only the exchange-token hash
- Approva still does not expose raw capability tokens through later reads
Use the raw capability token for the protected action:
curl -X POST "http://localhost:4000/v1/capabilities/use" \
-H "Authorization: Bearer approva_sk_..." \
-H "Content-Type: application/json" \
-d '{
"token": "cap_...",
"action": "deployment.execute",
"resource": {
"type": "service",
"id": "billing-api"
},
"params": {
"environment": "production",
"version": "2026.03.16-demo",
"region": "eu-west-1"
}
}'On success, Approva records capability.used in:
- the operational audit trail
- the immutable event log
- the ledger hash chain
After successful capability use, the machine client continues the protected action.
Examples:
- deploy the release
- execute the production migration
- issue the refund
- run the remote command
The important boundary is:
- Approva grants and records permission
- your agent or service performs the actual protected action
The recommended production-style pattern is:
- backend or agent creates the request
- human approves through the secure Approva approval page
- Approva sends a signed webhook to the backend or agent
- backend or agent exchanges the one-time token
- backend or agent uses the capability and continues
This keeps:
- approval auth separate from machine auth
- opaque capability tokens intact
- raw capability-token delivery explicit and time-bounded
- the exchange token is only delivered on the approved webhook path
- there is no later raw-token recovery by request id or capability id
- some examples in this repo are still intentionally simpler and poll-based for illustration, but the canonical automated continuation path is the signed webhook plus exchange-token flow
- Approva does not yet implement richer workload identity or per-workload trust policies
- machine scopes are intentionally small in the current open-core runtime