Skip to content

Auto-Approve

Auto-approve lets safe queries bypass the approval step while still recording them in the audit log. It uses risk scoring to determine what qualifies as “safe.”

Request arrives
Workflow matched
Has [workflows.auto_approve]?
│ │
No Yes
▼ ▼
Pending mode = "always"?
(needs human) ┌────┼────┐
Yes No (risk_based)
▼ ▼
AutoApproved Risk ≤ threshold?
┌────┼────┐
Yes No
▼ ▼
AutoApproved Pending

Auto-approve is configured as a sub-table within each workflow:

[[workflows]]
database = "*"
environment = "staging"
[workflows.auto_approve]
mode = "risk_based" # "always" or "risk_based"
risk = "low" # Maximum risk level (risk_based only)
allow_read_only = true # SELECT always counts as Low
allow_safe_ddl = true # CREATE TABLE/INDEX counts as Low
max_estimated_rows = 1000 # Row threshold for large-table risk
FieldTypeRequiredDefaultDescription
modeStringYes"always" (unconditional) or "risk_based" (conditional)
riskStringrisk_based onlyMax risk: low, medium, or high
allow_read_onlyBooleanNotrueIf true, SELECT is always Low risk
allow_safe_ddlBooleanNotrueIf true, CREATE TABLE/VIEW/INDEX is always Low risk
max_estimated_rowsIntegerNo1000Tables above this row count trigger higher risk

All requests matching this workflow are auto-approved unconditionally. No steps are needed.

[[workflows]]
database = "*"
environment = "development"
[workflows.auto_approve]
mode = "always"

Requests are auto-approved only if the assessed risk level is at or below the threshold. If risk exceeds the threshold, the request falls through to approval steps.

[[workflows]]
database = "*"
environment = "staging"
[workflows.auto_approve]
mode = "risk_based"
risk = "low"
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "dba"
min = 1

Important: risk_based mode requires [[workflows.steps]] — without steps, there’s no fallback when risk exceeds the threshold.

LevelMeaning
LowSafe operation (SELECT, safe DDL, small tables)
MediumModerate concern (1 warning, large table without cascade)
HighSignificant risk (DROP/TRUNCATE, multi-DML, cascade FK + large table, ≥3 warnings)
CriticalReserved for future use
UnknownSchema not synced — cannot assess risk
UnavailableParse failure — cannot classify

Important: Unknown and Unavailable are never auto-approved regardless of the risk threshold.

FactorTriggersResult
Read-onlySELECT + allow_read_only = trueLow
Safe DDLCREATE TABLE/VIEW/INDEX + allow_safe_ddl = trueLow
Schema not syncedAgent hasn’t synced schema yetUnknown
Multi-statement DML>1 DML statements in one requestHigh
DROP / TRUNCATEDestructive operations detectedHigh
≥3 SQL review warningsMultiple issues foundHigh
Cascade FK + large tableFK with CASCADE on table > max_estimated_rowsHigh
Cascade FK + small tableFK with CASCADE on table ≤ max_estimated_rowsMedium
Large tableTable > max_estimated_rows (without cascade)Medium
1-2 SQL review warningsMinor issues foundMedium
[[workflows]]
database = "*"
environment = "development"
[workflows.auto_approve]
mode = "always"

Auto-approve reads + safe operations on staging

Section titled “Auto-approve reads + safe operations on staging”
[[workflows]]
database = "*"
environment = "staging"
[workflows.auto_approve]
mode = "risk_based"
risk = "low"
allow_read_only = true
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "team-lead"
min = 1

Simply omit [workflows.auto_approve] from the production workflow:

[[workflows]]
database = "*"
environment = "production"
require_reason = true
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "dba"
min = 1

Use dbward policy resolve to see why a query was or wasn’t auto-approved:

Terminal window
dbward policy resolve --database app --environment staging