Skip to main content

GET /api/v1/approvals

List approval requests filtered by status.

Query Parameters

ParameterTypeDefaultDescription
statusstring"pending"Filter by status: "pending", "approved", "rejected"

Response

[
  {
    "id": "approval_001",
    "entityType": "qualificationRule",
    "entityId": "qr_001",
    "entityName": "Credit Score Rule",
    "action": "update",
    "status": "pending",
    "requesterId": "user_001",
    "requesterName": "John Doe",
    "payload": { "config": { "threshold": 650 } },
    "createdAt": "2026-03-15T10:00:00.000Z"
  }
]

POST /api/v1/approvals

Create a new approval request.

Request Body

FieldTypeRequiredDescription
entityTypestringYesEntity type (e.g., "qualificationRule", "contactPolicy", "decisionFlow", "offer")
entityIdstringYesID of the entity to modify
entityNamestringYesDisplay name of the entity
actionstringYes"create", "update", or "delete"
payloadobjectNoThe proposed changes to apply on approval
stagesArray<{ stageName, requiredRole }>NoOptional multi-stage chain. Each stage has stageName: string and requiredRole: "viewer" | "editor" | "admin". Decisions walk the stages in order. When omitted, one default admin stage is created. See Four-eyes governance.

Example

curl -X POST https://playground.kaireonai.com/api/v1/approvals \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "entityType": "qualificationRule",
    "entityId": "qr_001",
    "entityName": "Credit Score Rule",
    "action": "update",
    "payload": { "config": { "threshold": 650 } }
  }'
Response: 201 Created

GET /api/v1/approvals/

Get a single approval request.

POST /api/v1/approvals/

Resolve an approval request (approve or reject). Editors and admins can decide; the per-stage requiredRole is then enforced inside the walk. Viewers are 403’d at the route boundary. When the last stage of the chain is approved, the stored payload is automatically applied to the target entity. Supported entity types for auto-application: algorithmModel, qualificationRule, contactPolicy, decisionFlow, offer, creative, channel, rankingProfile. An unsupported entityType causes the whole transaction (stage update + parent flip + apply) to roll back — returning a 409 — so an approval cannot flip to approved without the entity actually changing.

Request Body

FieldTypeRequiredDescription
actionstringYes"approve" or "reject"
commentsstringNoReviewer comments

Response (approved)

{
  "id": "approval_001",
  "status": "approved",
  "approverId": "user_002",
  "approverName": "Admin",
  "comments": "Looks good, deploying to production",
  "resolvedAt": "2026-03-16T14:30:00.000Z"
}

Response (rejected)

{
  "id": "approval_001",
  "status": "rejected",
  "approverId": "user_002",
  "approverName": "Admin",
  "comments": "Threshold too aggressive, please revise",
  "resolvedAt": "2026-03-16T14:30:00.000Z"
}

Roles

EndpointAllowed Roles
GET /approvalsany authenticated
POST /approvalsany authenticated
GET /approvals/{id}any authenticated (returns parent + ordered stages array in one round-trip)
POST /approvals/{id} (resolve)editor or admin (per-stage requiredRole is enforced inside the walk)

Auto-expiry

Pending approvals that sit unresolved for longer than APPROVAL_MAX_AGE_HOURS (default 168 = 7 days) are auto-flipped to status = "expired" by the /api/v1/cron/approvals-expire endpoint. Wire it into your scheduler at any cadence — every 15 minutes is plenty since the operation is a single bulk update and idempotent. The cutoff is configurable per-deployment via the APPROVAL_MAX_AGE_HOURS env var. Expired requests retain their full audit trail; only status and resolvedAt change.

Four-eyes decision-flow publish gate

When a tenant sets requirePublishApproval: true (tenant settings), publishing a decision flow requires a dedicated approval request:
FieldValue
entityType"decisionFlow"
action"publish"
entityIdthe decision-flow id being published
curl -X POST https://playground.kaireonai.com/api/v1/approvals \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "entityType": "decisionFlow",
    "entityId": "df_001",
    "entityName": "Spring Campaign Flow",
    "action": "publish"
  }'
Unlike the auto-applied entity types above, a publish approval does not mutate the flow when it is approved — it simply records the second-person sign-off. The publish route checks for the latest approved request matching this shape, and one approval authorizes exactly one publish (the publish stamps the approval’s id onto the new version so it can’t be reused). Because stage walking rejects self-approval and duplicate approvers, an approved publish request guarantees two distinct identities. See also: Compliance · Cron jobs