Documentation

Review workflows.

Last updated

Review plans

What it is. The approval policy attached to a datasource — it decides how a submitted query gets from "submitted" to "executed". Use it to require AI scoring, one or more human sign-offs, or both, and to auto-approve low-risk reads. Each plan is a sequence of stages; each stage names approvers (by role or specific user) and a minimum number of approvals before the query advances.

Configure it. Build plans at /admin/review-plans — start from a built-in template or from scratch:

Review plans admin page with the create-from-template dropdown open, listing four templates — Strict (writes need 2 approvals), Lenient (reads auto-approved), AI-only (no human approval), and Standard (AI plus one reviewer).
/admin/review-plans → caret next to Add review plan exposes four built-in templates that prefill the create modal.
Add review plan modal with name and description fields, AI-review and human-approval toggles enabled, minimum approvals set to 1, approval timeout 24 hours, and an approver row defaulting to the REVIEWER role at stage 1.
/admin/review-plansAdd review plan. Stack approver rows to build multi-stage chains.
  1. Open /admin/review-plans and click Add review plan, or pick a built-in template from the dropdown caret next to it (Strict — writes need 2 approvals, Lenient — reads auto-approved, AI-only — no human approval, Standard — AI + 1 reviewer) to prefill the modal with sensible defaults.
  2. Name and describe the policy — e.g. "Production writes — two reviewers".
  3. Pick the gates. Toggle Require AI review to score every query before it queues for humans; toggle Require human approval to demand at least one reviewer sign-off. Auto-approve LOW-risk reads lets SELECTs skip humans entirely when AI risk is below the threshold.
  4. Set thresholds. Minimum approvals is the number of distinct reviewers needed before the query advances; Approval timeout (hours) is when AccessFlow auto-rejects an idle PENDING_REVIEW query (it scans on a cadence set by ACCESSFLOW_WORKFLOW_TIMEOUT_POLL_INTERVAL, default PT5M).
  5. Build the approver chain. Click Add approver for each stage; each row names a role (or a specific user) and a stage number. Stages advance sequentially, and a single REJECTED decision at any stage terminates the query.

What can a review plan control?

A review plan is six settings plus an approver chain. Together they decide whether a query needs AI scoring, how many humans must sign off, which of them, and what happens if nobody does. Every setting below is per-plan, and a plan attaches to a datasource.

Setting What it does When disabled / unset
Require AI review Every query is scored for risk before it queues for humans. The query skips PENDING_AI and goes straight to review.
Require human approval At least one reviewer must sign off before execution. The query is approved without a human — AI-only gating.
Auto-approve LOW-risk reads SELECTs below the AI risk threshold skip human approval. Reads queue for review like any other statement.
Minimum approvals How many distinct reviewers must approve before the query advances. Defaults to one approval per stage.
Approval timeout (hours) How long an idle PENDING_REVIEW query waits before auto-rejection. The query waits indefinitely for a decision.
Approver chain Ordered stages, each naming a role or a specific user as approver. No chain means no staged escalation — one flat approval step.

Stages advance sequentially and a single Reject at any stage terminates the query. A user can never approve their own query, whatever their role.

Reviewer decisions. Reviewers can Approve, Reject, or Request changes. Reject and Request changes both require a non-empty comment — the server enforces this (HTTP 400 VALIDATION_ERROR), and the UI disables the confirm button until the textarea is populated. The comment is persisted on the decision row, rendered on the rejected stage of the timeline on /queries/<id>, and surfaced to the submitter as a "Changes requested" alert whenever the latest decision is REQUESTED_CHANGES and the query is still PENDING_REVIEW. Approve still treats the comment as optional.

Query status transitions. A query moves through these states:

PENDING_AI → PENDING_REVIEW → APPROVED → EXECUTED
                           ↘ REJECTED   (manual reviewer rejection)
                           ↘ TIMED_OUT  (approval-timeout auto-reject)
PENDING_REVIEW → CANCELLED (submitter only)
APPROVED → FAILED (execution error)

A single REJECTED decision at any stage terminates the query. If a query sits in PENDING_REVIEW past the plan's approval timeout, AccessFlow auto-rejects it (it scans on a cadence set by ACCESSFLOW_WORKFLOW_TIMEOUT_POLL_INTERVAL, default PT5M). Auto-approve reads lets SELECTs skip human approval entirely; Require AI review still scores the read but won't block on a human.

Escalation & reminders

What it is. Between a request arriving and the approval timeout auto-rejecting it, nothing used to happen — a stalled chain was silent until the submitter found out by being rejected. Two optional settings on a review plan add the warning shots.

Where. AdminReview plans, alongside Approval timeout: Escalate after (hours) and Nudge every (hours). Both are blank by default, and blank means off — an existing plan behaves exactly as it did before you set them.

  • Escalate after — when nobody has decided within this window, the request is raised to the reviewers it is waiting on and your organization's admins, once. It appears with an escalation notice on the request page and goes out over the plan's notification channels; PagerDuty pages for it on channels that enable the REVIEW_STALLED trigger. It must be shorter than the approval timeout — a longer window could never fire, so AccessFlow rejects it rather than letting you save a setting that quietly does nothing.
  • Nudge every — re-notifies the reviewers who still have not decided, on this cadence. It reaches only the people already on the hook, never admins, and never pages: a reminder is not an incident.

What it does not do. Escalation is notify-only. It never changes who is allowed to approve, and it never approves, rejects, or times out anything — waiting must not become a way around the approvers you configured. The request stays exactly where it was, with the same people able to act on it.

Grouped requests have no plan of their own, so a bundle uses the shortest escalation window among its members' plans. A member whose plan has escalation switched off simply does not contribute one. For bundles the escalation is recorded on the request group and nothing more — no notification and no queue badge yet, since grouped requests have no notification path at all.

Out-of-office delegation

What it is. An approval chain moves at the speed of its slowest human. When a named reviewer goes on holiday, a request sits in PENDING_REVIEW until the approval timeout auto-rejects it. Delegation lets a reviewer hand their review duty to a named colleague for a set window instead.

Where. Any reviewer sets this themselves on Profile settingsOut-of-office delegation. No admin involvement, and no permission is required to delegate — a delegation from someone with no review rights simply confers nothing.

How to set one up. Choose a colleague, optionally narrow the delegation to a single datasource, pick a start and end time, and save. During the window the delegate becomes an eligible approver everywhere you were — query review, governed API requests, and grouped requests — and the requests show up in their review queue with a Delegated tag. Revoking is immediate and takes effect on the next decision; the record itself is kept as evidence for anything already approved under it.

What it cannot do. These limits are enforced by the server, not the interface:

  • A delegation never grants a permission. The delegate still needs review rights of their own; delegation only widens which requests they may act on.
  • The delegate can never act on a request the delegator submitted — the no-self-approval rule follows the borrowed identity, not just the person clicking.
  • Delegation does not chain. If A delegates to B and B delegates to C, C gains nothing from A.
  • One human still gets one vote. Covering for two absent approvers at once does not let someone satisfy a two-approval requirement alone.
  • It stops the moment either party is deactivated, including via SCIM deprovisioning.

Audit. Every decision made under a delegation records both people — who clicked approve, and whose authority they used — so the trail never implies the absent reviewer acted. Admins can see every delegation in the organization under Admin, which is what makes an “on behalf of” entry interpretable months later.

Routing policies

What it is. Policy-as-code that decides a query's path automatically, after AI analysis and before reviewers see it. Use it to auto-approve routine reads, hard-block dangerous patterns, or escalate sensitive ones — instead of sending everything through the same review plan. Policies run in ascending priority and the first enabled one whose condition matches wins; anything unmatched falls through to the datasource's review plan exactly as before.

Configure it. Manage them at /admin/routing-policies (the Routing policies entry in the Security nav group):

  1. Open /admin/routing-policies (the Routing policies entry in the Security nav group, next to Review plans) and click Add policy.
  2. Name the policy and optionally scope it to one datasource — leave the datasource blank for an org-wide rule. Set its priority (unique per organisation; lower runs first) and the enabled toggle.
  3. Build the condition with the guided builder: pick match ALL (AND) or match ANY (OR), then add leaf conditions — each can be negated (NOT). Operands include query type, referenced tables (glob, e.g. payroll.*), AI risk level, AI risk score (with a comparison operator), requester role, requester group, time-of-day window, day-of-week, presence of a WHERE clause, presence of a LIMIT clause, the transactional (BEGIN…COMMIT) flag, and the pre-flight cost estimate — estimated rows (comparison against the engine's own EXPLAIN estimate, or the exact affected-row count for UPDATE/DELETE) and scan type (glob match on the plan's root operation, e.g. Seq*) — so a policy can route a 10-million-row sequential-scan DELETE differently from a 10-row indexed one.
  4. Choose the action. Auto-approve (skip human review), Auto-reject (block the query), Require approvals (force human review with an absolute minimum number of approvers), or Escalate (force human review, adding a delta on top of the review plan's minimum). The approver count applies only to the last two actions.
  5. Reorder policies any time with the per-row up/down controls — the order is the evaluation order.
How it routes. Time-of-day and day-of-week conditions are evaluated in the server's local timezone (overnight windows wrap around midnight). On datasources with AI analysis disabled, risk-based conditions never match (there's no AI signal); routing does not run when AI analysis fails — the query goes to a human instead. The cost-estimate conditions likewise never match while no estimate exists (engine without a plan concept, or the estimate failed) — they fail closed rather than auto-approving blind. Every automated decision is recorded in the audit log (QUERY_APPROVED / QUERY_REJECTED with source: "ROUTING_POLICY"), and the query detail page shows which policy matched. Routing policies are managed via the ADMIN-only /api/v1/admin/routing-policies CRUD and /reorder endpoints.
The /admin/routing-policies page listing ordered routing policies by priority — name, scope, condition summary, action (auto-approve / escalate), and per-row reorder controls.
/admin/routing-policies — ordered, attribute-based auto-decision rules; first match by priority wins, unmatched falls through to the review plan.

Access recertification campaigns

What it is. Recurring attestation campaigns that make someone periodically re-confirm who still needs standing datasource access — the review control SOC 2 and ISO 27001 auditors ask for. An admin schedules an org- or datasource-scoped campaign; when it opens it snapshots the current standing grants into one item per grant and notifies the eligible reviewers (multi-channel, plus an attestation.campaign_opened WebSocket event). Reviewers work a certify / revoke worklist that reuses the review-queue patterns (self-review blocked, bulk-certify); a revoke routes through the normal permission-revoke path, so access is actually removed.

Configure it. Manage campaigns from /admin/attestation and certify items from the reviewer worklist at /reviews/attestations. Two clustered-safe jobs run the lifecycle: one opens SCHEDULED campaigns at their scheduled_open_at, the other closes OPEN campaigns at their due_at and applies each campaign's pending-default (KEEP or REVOKE) to anything a reviewer never got to. A completed campaign exports as a CSV evidence file (who reviewed what, decisions, timestamps), and every transition — ATTESTATION_CAMPAIGN_OPENED/CLOSED, ATTESTATION_ITEM_CERTIFIED/REVOKED — lands in the tamper-evident audit log.

Tune it. ACCESSFLOW_ATTESTATION_OPEN_POLL_INTERVAL and ACCESSFLOW_ATTESTATION_CLOSE_POLL_INTERVAL (open / close scan cadence, both default PT5M) and ACCESSFLOW_ATTESTATION_MAX_EVIDENCE_ROWS (row cap before an evidence CSV is marked truncated, default 50000).

The AccessFlow access-recertification campaign list at /admin/attestation, showing scheduled and open attestation campaigns with their due dates and pending-item counts.
/admin/attestation — admins schedule recurring access-recertification campaigns; reviewers certify or revoke each snapshotted grant.

Unused-grant analytics. Certifying access is easier when you know which grants nobody is using, so a clustered-safe background job folds the audit log's executed queries and executed API calls into a per-grant usage summary — last used, how often, and which of the granted tables (datasource grants) or operations (API-connector grants) were ever actually exercised — and labels each grant NEVER_USED, STALE, OVER_SCOPED, ACTIVE, or INSUFFICIENT_DATA. The label shows on every attestation item and sorts the reviewer worklist staleness-first, so never-used grants land on page one; admins and auditors also get the standing report at /admin/over-provisioned-access (filter by resource, holder, or recommendation, and export as CSV — gated on the ACCESS_USAGE_REPORT_VIEW permission, held by ADMIN and AUDITOR), and an opt-out notification nudges org admins once per cooldown when a grant crosses the staleness threshold. It is advisory only: nothing is ever revoked automatically, and a recommendation is never an input to routing, grant coverage, or any other decision path. Grants inherited from a user group are not covered.

Tune it. Grant-usage analytics reads nothing but the audit log, so the knobs are all about how far back it looks and how patient it is (defaults shown):

  • Cadence & window: ACCESSFLOW_ACCESS_USAGE_AGGREGATION_POLL_INTERVAL (PT1H — how often the job folds new audit events), ACCESSFLOW_ACCESS_USAGE_BACKFILL_WINDOW (P90D — how far back a newly-summarised grant starts observing).
  • Verdict thresholds: ACCESSFLOW_ACCESS_USAGE_STALENESS_THRESHOLD (P60D — unused for longer than this is STALE), ACCESSFLOW_ACCESS_USAGE_MIN_OBSERVATION_WINDOW (P14D — below this a grant is INSUFFICIENT_DATA rather than NEVER_USED), ACCESSFLOW_ACCESS_USAGE_OVER_SCOPED_THRESHOLD (0.5 — exercised/granted ratio below which an active grant is OVER_SCOPED).
  • Caps: ACCESSFLOW_ACCESS_USAGE_MAX_ROWS_PER_TICK (50000 — audit events folded per organization per run; the remainder is picked up next run), ACCESSFLOW_ACCESS_USAGE_MAX_TRACKED_TARGETS (200 — distinct exercised tables/operations retained per grant), ACCESSFLOW_ACCESS_USAGE_MAX_REPORT_ROWS (50000 — row cap before a report CSV is marked truncated).
  • Staleness nudge: ACCESSFLOW_ACCESS_USAGE_NUDGE_ENABLED (true — set false to switch the notification off), ACCESSFLOW_ACCESS_USAGE_NUDGE_COOLDOWN (P30D — minimum time between nudges for the same grant).

Deployment pipelines

What it is. The same review machinery, applied to CI/CD deployments. A deployment pipeline names a provider (GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Bitbucket Pipelines, or generic) and holds ordered environmentsdev, staging, production — each of which can require review, set its own approval count, override the review plan, and opt in to break-glass. A CI job asks AccessFlow for permission to release, the request flows through AI release-risk analysis, routing policies and human approval, and the job blocks on a fail-closed gate until every required approval is granted. The submitter — for a pipeline, the user who owns the API key — can never approve their own deployment.

The /deployments page listing deployment requests — pipeline, environment, version, status, risk and submitted time, with status and environment filters above the table.
/deployments — every deployment you submitted, with its status, target environment and release version.
A deployment request detail page showing the pipeline, target environment, version and commit, the AI release-risk verdict, the approval timeline and the releasability banner.
/deployments/<id> — release metadata, the AI risk verdict, the approval timeline and the fail-closed releasability banner.
The /deployment-reviews queue listing deployments awaiting a decision — pipeline, environment, version, submitter, risk and age — with approve and reject actions per row.
/deployment-reviews — the reviewer queue. A deployment never appears here for its own submitter.

Configure it. Create pipelines at /admin/deployment-pipelines (requires the DEPLOYMENT_PIPELINE_MANAGE permission, held by ADMIN). Each pipeline's settings page carries six tabs. Alongside General and the CI setup snippet sit Environments (promotion order, required approvals, review-plan override, break-glass opt-in), Permissions (per-user and per-group can_trigger / can_break_glass grants with an optional expiry — a user's effective grant is the most-permissive union of their direct grant and every unexpired group grant), Freeze windows, and Routing policies. The CI setup tab renders a copy-paste snippet for the pipeline's provider. Reviewers work deployments at /deployment-reviews (requires DEPLOYMENT_REVIEW, held by ADMIN and REVIEWER); everyone sees their own at /deployments.

The /admin/deployment-pipelines page listing configured pipelines — name, CI provider, repository, environment count and whether AI analysis is enabled.
/admin/deployment-pipelines — one row per pipeline, with its CI provider and environment count.
The Environments tab of a deployment pipeline settings page, listing ordered environments with their required approval count, review requirement, review-plan override and break-glass opt-in.
Pipeline settings → Environments — promotion order, required approvals, review-plan override and break-glass opt-in, per environment.

Freeze windows. A freeze window suspends releases for the whole organization, one pipeline, or a single environment. It is either one-off (a start and end instant) or recurring weekly (days of the week plus a start and end time in a named IANA timezone — an end time before the start time spans midnight). HOLD lets a deployment be submitted, analyzed and approved but withholds releasability until the window closes, so the release proceeds by itself afterwards with no re-approval. REJECT auto-rejects at submission instead. Where several windows are active at once the most specific scope wins, and REJECT beats HOLD. A window whose definition cannot be evaluated — a bad timezone, say — counts as an active HOLD, never a REJECT: a broken rule can stall a release but can never destroy a request.

The Freeze windows tab of a deployment pipeline settings page, listing one-off and recurring weekly windows with their scope, behavior (hold or reject), reason and enabled state.
Pipeline settings → Freeze windows — one-off or recurring weekly, scoped org-wide, per pipeline or per environment, holding or rejecting.

Break-glass and rollbacks. An emergency deploy needs both a can_break_glass grant on the pipeline and allow_break_glass on the target environment — for everyone, with no admin bypass. It skips AI analysis, routing and review, and pays for that with an instant notification to every org admin, a prominent audit row naming any freeze window it bypassed, and a mandatory retro-review an admin who is never the submitter must acknowledge. After a deploy the pipeline reports its outcome; a rollback on a review-required environment opens a follow-up review in the reviewer queue that the submitter can never close.

The rollback worklist tab of the deployment review queue, listing executed deployments reported as rolled back and awaiting a follow-up acknowledgement.
/deployment-reviews?tab=rollbacks — the follow-up worklist a rolled-back deployment opens.

Wire up CI. See the deployment gate in CI for the GitHub Action, the GitLab CI template, the Azure Pipelines step template, and the plain-curl path, plus how to mint the service-account API key a pipeline authenticates with.

The CI setup tab of a deployment pipeline settings page, rendering a copy-paste CI snippet for the pipeline provider along with the pipeline identifier the job needs.
Pipeline settings → CI setup — a copy-paste snippet for the pipeline's provider, carrying the pipeline id the job gates on.

Tune it. ACCESSFLOW_DEPLOYGOV_TIMEOUT_CHECK (how often deployments awaiting review are checked against their review plan's approval timeout, default PT5M) and ACCESSFLOW_DEPLOYGOV_RELEASE_CHECK (how often approved deployments are scanned to announce that they have become releasable, default PT1M). Every transition lands in the audit log.