Human-in-the-Loop Approvals
Hold an agent's writes and DDL until a human approves them. Approve or reject in the dashboard, set auto-approve rules for safe changes, and auto-expire stale requests.
Approvals hold a statement before it reaches your database and wait for a human to say yes. Turn it on for a credential, and every write or DDL statement it runs is parked as an approval request instead of executing. A reviewer approves or rejects it in the dashboard, the agent's session blocks until then, and the statement runs only on approval.
This is the middle ground between read-only (no writes ever) and read-write (any write, immediately). The agent stays productive: it can draft a change and hand it to a person, rather than being told no.
Approvals apply to agent credentials and to human credentials. A junior analyst's write can be held for a senior reviewer the same way an agent's is.
Turn it on
Add an approval rule to a policy. It names which statement kinds are held.
pgbeam policies create writer-supervised \
--mode read-write \
--allow public.orders \
--hold writes,ddl \
--approval-expiry 1hOn the policy profile, open Approvals, choose which statement kinds to hold (writes, DDL, or both), and set an expiry. Pending requests show up on the Approvals tab with the SQL, the credential, and a diff of what the statement would change.
{
"name": "writer-supervised",
"mode": "read-write",
"allow": ["public.orders"],
"approvals": {
"hold": ["writes", "ddl"],
"expiry_seconds": 3600,
"auto_approve": [
{ "table": "public.orders", "max_rows": 10 }
]
}
}What the agent sees
When a held statement is parked, the agent gets an LLM-readable notice on the wire and the session waits:
NOTICE: statement held for approval (request apr_7f3a). Waiting for a reviewer.If the request is approved, the statement runs and the agent gets its result. If it is rejected or expires, the agent gets an error explaining why, so it can move on instead of hanging.
ERROR: statement rejected by reviewer (request apr_7f3a)
ERROR: approval request apr_7f3a expired after 1hApprove or reject a request
Reviewers act in the dashboard, or from the API for automation:
# Approve
curl -X POST \
"https://api.pgbeam.com/v1/projects/{projectId}/approvals/{approvalId}/approve" \
-H "Authorization: Bearer $PGBEAM_TOKEN"
# Reject with a reason
curl -X POST \
"https://api.pgbeam.com/v1/projects/{projectId}/approvals/{approvalId}/reject" \
-H "Authorization: Bearer $PGBEAM_TOKEN" \
-H "Content-Type: application/json" \
--data '{"reason": "touches too many rows, scope it down"}'Auto-approve rules
Holding every write on a busy credential is noise. Auto-approve rules let the safe changes through and reserve human attention for the rest. A rule matches on the table and a row ceiling: a write that touches fewer rows than the ceiling is approved automatically; anything larger is held.
"auto_approve": [
{ "table": "public.orders", "max_rows": 10 },
{ "table": "public.tags", "max_rows": 100 }
]PgBeam counts the rows a write would affect before it commits. A write under the ceiling is approved and recorded; a write over it is parked for a person. DDL is never auto-approved.
Auto-expire
A held request that no one acts on expires after the policy's expiry window. The
statement is rejected, the agent is told, and the request closes. This keeps a
forgotten request from holding an agent session open forever. Every approval,
rejection, and expiry is written to the audit log and can fire
an approval_requested webhook.
Related
- Read-only enforcement: block writes entirely instead of holding them.
- Sandbox writes: let an agent write freely against a throwaway branch.
- Audit log: every approval decision is recorded.
- Audit export: fire a webhook when a request is created.