Skip to main content
Reviewers use this endpoint to approve or deny queued agent actions. After a decision, Sentrail either executes the stored deferred action (on approve) or marks it failed (on deny). Base URL: https://<project-ref>.supabase.co/functions/v1/approval-decision Auth: Supabase user JWT (not an agk_ API key). Caller must have reviewer, admin, or owner role in the workspace.

Request

POST /functions/v1/approval-decision
Authorization: Bearer <supabase-jwt>
Content-Type: application/json

Body

FieldTypeRequiredDescription
approvalRequestIdstring (uuid)YesThe ID from the 202 approval response
decisionstringYes"approved" or "denied"
reasonstringNoReviewer’s note, stored in review_reason
curl -X POST \
  "https://<project-ref>.supabase.co/functions/v1/approval-decision" \
  -H "Authorization: Bearer <supabase-jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "approvalRequestId": "550e8400-e29b-41d4-a716-446655440000",
    "decision": "approved",
    "reason": "Reviewed and looks correct"
  }'

Success response

// HTTP 200
{
  "ok": true,
  "status": "executed",
  "executionResult": {
    "id": 15,
    "html_url": "https://github.com/owner/repo/pull/15",
    "number": 15
  },
  "notificationQueued": true,
  "notificationError": null
}
FieldTypeDescription
okbooleantrue on success
statusstringFinal status: executed, failed, denied
executionResultobject | nullTool API response (on approve + successful execution)
notificationQueuedbooleanWhether notification delivery was attempted
notificationErrorstring | nullNotification error message, if any

What happens on approve

  1. approval_requests.statusapproved
  2. approval_requests.reviewed_by and reviewed_at are set
  3. An audit log entry is written (decision = allow)
  4. executeDeferredAction replays the stored HTTP call to the tool API
  5. approval_requests.execution_result is set to the tool response
  6. approval_requests.statusexecuted
  7. A notification is queued for the approval_decided event

What happens on deny

  1. approval_requests.statusdenied
  2. An audit log entry is written (decision = block)
  3. deferred_actions.statusfailed
  4. A notification is queued for the approval_decided event

Error responses

// HTTP 400 — invalid payload
{ "ok": false, "error": "Invalid request payload" }

// HTTP 401 — not authenticated
{ "ok": false, "error": "Authentication required" }

// HTTP 403 — not a reviewer
{ "ok": false, "error": "Reviewer role required" }

// HTTP 404 — approval not found
{ "ok": false, "error": "Approval request not found" }

// HTTP 409 — already decided
{ "ok": false, "error": "Approval request was already decided" }

// HTTP 500 — execution failed
{ "ok": false, "error": "Approval recorded but deferred execution failed" }

Status codes

CodeMeaning
200Decision recorded and (if approved) executed
400Invalid request body
401Not authenticated
403Reviewer role required
404Approval request not found
405Method not allowed
409Approval already has a decision
500Database or execution error