Skip to content

Approval

Human-in-the-loop gate for AI agents. One decorator, full loop.

Decorator

from primitif.approval import require_approval

@require_approval
def deploy(service, branch):
    run_pipeline(service, branch)

req = deploy("api-gateway", "main")
print(req.status)        # "pending"
print(req.approval_url)  # share with a human

The function doesn't run. Instead, it returns an ApprovalRequest with a URL for a human to approve or reject.

Dispatch on webhook

When the human decides, your webhook handler runs the original function:

from flask import Flask, request
from primitif import approval, verify_webhook

app = Flask(__name__)

@app.post("/webhook/approval")
def handle():
    event = verify_webhook(
        body=request.data,
        signature=request.headers["X-Webhook-Signature"],
        secret="whsec_...",
    )
    approval.dispatch(event)  # runs deploy() with original args
    return "", 200

Without decorator

from primitif import approval

req = approval.create_request(
    "Delete user account #1234",
    context={"user_id": 1234, "reason": "GDPR request"},
    callback_url="https://myapp.com/webhooks/approval",
)
print(req.approval_url)

Check status

status = approval.get_request(req.id)
print(status.status)  # "pending", "approved", "rejected", "expired"

List requests

for req in approval.list_requests(status="pending"):
    print(req.action, req.approval_url)

Decorator options

@require_approval(
    action="Deploy to production",     # custom action name
    requested_by="deploy-agent",       # who's asking
    callback_url="https://myapp.com/hook",  # webhook URL
)
def deploy(service, branch):
    run_pipeline(service, branch)