This guide shows the first practical backend integration path for Approva using the example app in:
Canonical reference:
The example simulates a deploy agent that wants to execute:
- action:
deployment.execute - resource:
service/billing-api - params:
environment = productionversion = 2026.03.16-demoregion = eu-west-1
The flow is:
- the agent requests approval from Approva
- Approva returns a pending approval request and secure approval URL
- a human opens the approval page and approves with a passkey
- Approva sends a signed webhook to the example app with a one-time capability exchange token
- the example exchanges that token through
POST /v1/capabilities/exchange - the example calls
POST /v1/capabilities/use - the example then simulates
deployment executed
From the repo root:
docker compose up -d postgres
cp .env.example .env
cp apps/api/.env.example apps/api/.env
cp apps/approval-ui/.env.local.example apps/approval-ui/.env.local
pnpm install
pnpm prisma generate
pnpm db:push
pnpm db:seed
pnpm devIn another terminal:
pnpm --filter @approva-example/node-deploy-agent devOpen:
Use the example page button or:
curl -X POST http://localhost:4100/request-approvalThe response includes:
approvalRequestIdapprovalUrl
Open the approvalUrl, then:
- register a passkey locally if needed
- authenticate with the passkey
- approve the request
The example app receives:
approval_request.approvedapproval_request.rejectedapproval_request.expired
It verifies the webhook signature before processing.
When the request is approved in exchange_token mode, the webhook payload includes:
capabilityIdcapabilityExchangeTokencapabilityExchangeExpiresAt
The example then:
- calls
POST /v1/capabilities/exchange - receives the raw opaque capability token once
- calls
POST /v1/capabilities/use - simulates
deployment executed
Create an approval request directly against Approva:
curl -X POST "http://localhost:4000/v1/approval-requests" \
-H "Authorization: Bearer approva_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: node-deploy-agent-001" \
-d '{
"externalRequestId": "node-deploy-agent-001",
"requestedBy": {
"system": "node-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": "http://localhost:4100/webhooks/approva",
"deliverCapabilityMode": "exchange_token"
}
}'Equivalent SDK example:
import { ApprovalClient } from '@approva/sdk';
const approva = new ApprovalClient({
baseUrl: 'http://localhost:4000',
apiKey: process.env.APPROVA_API_KEY,
});
const approval = await approva.requestApproval(
{
externalRequestId: 'node-deploy-agent-001',
requestedBy: {
system: 'node-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: 'http://localhost:4100/webhooks/approva',
deliverCapabilityMode: 'exchange_token',
},
},
{
idempotencyKey: 'node-deploy-agent-001',
},
);
console.log(approval.request.id);
console.log(approval.approvalUrl);curl -X POST "http://localhost:4000/v1/capabilities/use" \
-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"
}
}'curl -X POST "http://localhost:4000/v1/capabilities/exchange" \
-H "Authorization: Bearer approva_sk_..." \
-H "Content-Type: application/json" \
-d '{
"exchangeToken": "cex_..."
}'- approval access token and approver passkey authentication are separate layers
- capability tokens are opaque and only
sha256(token)is stored by Approva - later fetches still do not reveal raw capability tokens by id
- the supported machine continuation path is the short-lived single-use exchange token delivered on the approved webhook