# Approval workflow

> Roles, connection grants, and the lifecycle of a query request from submission through approval to masked, audited results.

- Documentation: Guides
- Updated: 2026-09-07
- Source: https://queryproxy.com/docs/approval-workflow/
- Language: en-US
- Author: Muhammet ŞAFAK

---
Everything in QueryProxy revolves around one object: the **query request**. This
guide walks through who can do what, and what happens to a request at each step.

## Roles

| Role | Can do |
| :-- | :-- |
| **Admin** | Everything, system-wide: teams, users, all settings. Set per user (`is_admin`). |
| **DBA** | Manage connections and masking rules, grant access, approve/reject requests. |
| **Developer** | Submit queries through granted connections, view own (masked) results. |
| **Auditor** | Read-only access to the audit log and request histories. |

Admin is global; DBA, Developer and Auditor are **per team**. A user can belong
to several teams with different roles. Connections, requests, masking rules and
audit logs never cross team boundaries.

## Connections and grants

DBAs register target databases under **Connections**. Credentials (host, port,
database, username, password) are encrypted at rest with AES-256 and are never
shown again after saving — they never appear in logs or error messages either.

A developer sees a connection only after a DBA **grants** it to them
(Connections → Grants). Ungranted connections are invisible in Query Studio.

## The request lifecycle

```
pending ──▶ approved ──▶ queued ──▶ running ──▶ completed
   │                                              │
   ├──▶ rejected  (reason required)               └──▶ failed
   └──▶ cancelled (by the requester)
```

- **Submit.** The developer writes SQL in Query Studio. The
  [SQL guards](/docs/sql-guards/) validate it before submission — requests
  that violate a guard never enter the queue.
- **Pending.** The request waits indefinitely until someone decides; there is
  no auto-expiry. Team DBAs are notified in the portal and, if configured, in
  [Slack](/docs/slack-integration/) / [Teams](/docs/teams-integration/).
- **Approve / Reject.** Any team DBA can decide — except on their **own**
  requests: self-approval is blocked. Admins may override that block, and the
  override is flagged in the audit log. Rejection requires a written reason.
- **Execute.** Approved requests are queued and picked up by the worker. Reads
  stream through a database cursor into a result file, with
  [masking](/docs/data-masking/) applied to every row before it is written.
  Writes report affected row counts; multi-statement requests run atomically
  and roll back entirely if any statement fails.
- **Results.** The requester (and the team's DBAs) can browse results
  page-by-page or download them as CSV. Auditors see request metadata, history
  and approval traces — not the result data itself. Result files are pruned
  automatically after the configured retention period.

## Notifications

Requesters are notified when their request is decided and when execution
finishes; DBAs are notified of new submissions. Notifications appear in the
portal's bell menu and, when configured, in your chat channel.

## The audit trail

Every step above writes an audit entry: submission, decision (with channel and
override flag), execution start/finish (with duration and affected rows),
result downloads, plus logins, grants and configuration changes. Audit rows are
immutable — the model layer refuses updates and deletes. Auditors can filter
the trail and export it as CSV.
