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 only—Max 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