Skip to main content

DQ Cloud API (0.1.0)

Download OpenAPI specification:Download

Self-hostable data-quality platform: connect data sources, profile data assets, add rules, bundle them into validations, run them on a schedule, and review execution runs, results, anomalies, and alerts. Authenticate every request with a Bearer token: a personal API key (dqk_…) or a session access token.

agent

Agent Register Datasource

Agent registers one of its locally-configured datasources.

Authorizations:
HTTPBearer
Request Body schema: application/json
required
object (Display Metadata)
Default: {}
name
required
string (Name)
type
required
string (Type)

Responses

Request samples

Content type
application/json
{
  • "display_metadata": { },
  • "name": "string",
  • "type": "string"
}

Response samples

Content type
application/json
null

Agent Deregister Datasource

Agent deregisters one of its datasources.

Authorizations:
HTTPBearer
path Parameters
datasource_id
required
string <uuid> (Datasource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Agent Update Datasource

Agent updates display_metadata on its datasource.

Authorizations:
HTTPBearer
path Parameters
datasource_id
required
string <uuid> (Datasource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Agent List Jobs

Return up to 20 pending agent-mode jobs that target this agent's datasources. Oldest first so backlog clears in order.

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Agent Claim Job

Atomic claim. Returns the dispatch envelope on success; 409 if another agent already won the job (the same agent re-calling will also see 409, on purpose — claim must be idempotent against self).

Authorizations:
HTTPBearer
path Parameters
run_queue_id
required
string <uuid> (Run Queue Id)

Responses

Response samples

Content type
application/json
null

Agent Complete Job

Accept the redacted metadata payload, write a ValidationResult row, mark the queue row success/failed, fire alert evaluation, audit the call.

Three layers of defence on the payload:

  1. Body-size cap (_MAX_COMPLETE_PAYLOAD_BYTES) — even a healthy redacted result is well under 8 KiB; anything bigger is suspicious.
  2. :func:validate_redacted_payload — rejects forbidden keys (unexpected_rows, kwargs, value_set, …).
  3. :func:_truncate_payload_strings — caps the user-controlled string fields so description-style exfiltration can't fit more than a few hundred chars of customer data.

Then the host-side belt-and-suspenders: the queue row's checkpoint must target one of this agent's own datasources in this principal's workspace (:func:_run_queue_targets_agent).

Authorizations:
HTTPBearer
path Parameters
run_queue_id
required
string <uuid> (Run Queue Id)
Request Body schema: application/json
required
required
object (Data)
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": { }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Agent Fail Job

Mark the queue row failed with the agent-provided error (scrubbed to 2000 chars). Used when the agent can't reach its DB, GX raises, etc.

Authorizations:
HTTPBearer
path Parameters
run_queue_id
required
string <uuid> (Run Queue Id)
Request Body schema: application/json
required
required
object (Data)
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": { }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Agent Heartbeat Job

Roll claimed_at forward so a long-running job doesn't get reclaimed.

Authorizations:
HTTPBearer
path Parameters
run_queue_id
required
string <uuid> (Run Queue Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

audit-exports

Download Audit Export

Stream the rendered export file.

Authenticated by the HMAC signature in the query string (see :func:services.export_storage.verify_download_token). Deliberately not gated by Depends(get_principal) — the signed URL is the credential, designed to be hand-able to an auditor without forcing them to log into DQ Cloud. The signature expires per the URL's own exp claim; past the expiry, every request returns 410 Gone.

path Parameters
export_id
required
string <uuid> (Export Id)
query Parameters
sig
required
string (Sig) [ 10 .. 128 ] characters
exp
required
integer (Exp) >= 0

Responses

Response samples

Content type
application/json
null

List Audit Exports

List the org's most recent exports.

Hard-capped at 30 rows — the UI surface is "what's pending and what can I still download", not an audit-trail of every export ever requested. A future GET ?cursor=... would extend this; not in scope here.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
limit
integer (Limit) [ 1 .. 30 ]
Default: 30

Responses

Response samples

Content type
application/json
null

Create Audit Export

Request a new audit-log export.

Inserts an AuditExport row in status='pending' and enqueues run_audit_export on ARQ. Returns 202 with the row + a poll_url (the single-row GET) so the UI can wire up the polling loop without having to construct the URL itself.

Date-range cap is :data:settings.audit_export_max_range_days (default 90). Years-long exports are out of scope; an operator wanting one requests it in 90-day slices.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
end_date
required
string <date> (End Date)
format
required
string (Format)
start_date
required
string <date> (Start Date)

Responses

Request samples

Content type
application/json
{
  • "end_date": "2019-08-24",
  • "format": "string",
  • "start_date": "2019-08-24"
}

Response samples

Content type
application/json
null

Get Audit Export

Single-row status check — used by the UI's poll loop.

Returns the row plus, when status='complete', a freshly-signed download URL. The URL is regenerated on every read (rather than being persisted) so the underlying signing key can be rotated without invalidating already-issued export rows the moment the config flips.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
export_id
required
string <uuid> (Export Id)

Responses

Response samples

Content type
application/json
null

deployment

Deployment metadata

Public, unauthenticated deployment metadata.

Returned fields:

  • region — operator-declared region label (DEPLOYMENT_REGION).
  • version — the API package version (matches app.version).
  • sso_enabled — whether SSO is available at this deployment.
  • llm_provider — the default LLM provider, or None when AI is not configured.

Responses

Response samples

Content type
application/json
{
  • "property1": { },
  • "property2": { }
}

Deployed build

Public, unauthenticated build marker (C-84-1).

Returns the commit sha the running API image was built from (stamped per deploy from image_refs.hcl), its short form, the commit timestamp and the package version. The UI shows the same sha in the account menu. A commit sha of an open-source-shaped codebase is not sensitive; nothing else is returned.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

expectations

organisations

Patch Organisation

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Audit Retention Days (integer) or Audit Retention Days (null) (Audit Retention Days)
Any of
>= 0
integer (Audit Retention Days) >= 0

Responses

Request samples

Content type
application/json
{
  • "audit_retention_days": 0
}

Response samples

Content type
application/json
null

agent-tokens

List Agent Tokens

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Agent Token

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
name
required
string (Name)
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
null

Revoke Agent Token

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
token_id
required
string <uuid> (Token Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

admin-llm

Get Ai Autorun

Which AI features auto-run for this org (asks U4).

Readable by EVERY authenticated org member — unlike ai-policy (LLM managers only) — because every AI panel reads it to decide whether to generate on mount. It exposes no secret and no spend figure, just the per-feature switches: the org's own choice (source="org") or, until an admin saves one, the deployment default (source="deployment").

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Update Ai Autorun

Save the org's AI auto-run switches (asks U4) — admin-gated.

Same gate as ai-policy (CAN_MANAGE_LLM_CONFIGS): the switches decide whether LLM spend happens without a click, so only the people who manage the org's AI engine may flip them. Not fresh_auth — it flips routing flags, not a credential. Partial: an omitted flag keeps its current value. Audited.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Asset Summary (boolean) or Asset Summary (null) (Asset Summary)
Explain (boolean) or Explain (null) (Explain)
Failures Review (boolean) or Failures Review (null) (Failures Review)
Rca (boolean) or Rca (null) (Rca)
Rule Suggestions (boolean) or Rule Suggestions (null) (Rule Suggestions)

Responses

Request samples

Content type
application/json
{
  • "asset_summary": true,
  • "explain": true,
  • "failures_review": true,
  • "rca": true,
  • "rule_suggestions": true
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Ai Policy

Return the org's stored ai_mode plus deployment native-availability.

native_available lets the UI decide whether to even offer the native toggle (a self-host without Vertex access shows BYOLLM only), and effective_mode is the engine the resolver would pick right now.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "property1": {
    },
  • "property2": {
    }
}

Update Ai Policy

Set (or clear, with ai_mode: null) the org's native-vs-BYOLLM override.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Ai Mode (string) or Ai Mode (null) (Ai Mode)
Any of
string (Ai Mode)
Enum: "native" "byollm"

Responses

Request samples

Content type
application/json
{
  • "ai_mode": "native"
}

Response samples

Content type
application/json
{
  • "property1": {
    },
  • "property2": {
    }
}

List Llm Configs

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Create Llm Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Api Key (string) or Api Key (null) (Api Key)
Base Url (string) or Base Url (null) (Base Url)
is_default
boolean (Is Default)
Default: false
model
required
string (Model)
name
required
string (Name)
provider
required
string (Provider)
Enum: "openai" "anthropic" "azure_openai" "ollama" "custom" "gemini" "google_ai"

Responses

Request samples

Content type
application/json
{
  • "api_key": "string",
  • "base_url": "string",
  • "is_default": false,
  • "model": "string",
  • "name": "string",
  • "provider": "openai"
}

Response samples

Content type
application/json
{
  • "base_url": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_default": true,
  • "model": "string",
  • "name": "string",
  • "provider": "openai"
}

Delete Llm Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
config_id
required
string <uuid> (Config Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Llm Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
config_id
required
string <uuid> (Config Id)
Request Body schema: application/json
required
Api Key (string) or Api Key (null) (Api Key)
Base Url (string) or Base Url (null) (Base Url)
Is Default (boolean) or Is Default (null) (Is Default)
Model (string) or Model (null) (Model)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "api_key": "string",
  • "base_url": "string",
  • "is_default": true,
  • "model": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

ai-usage

Get Org Ai Usage

Return this org's AI usage aggregated by day + a totals block.

Wire shape::

{
  "data": {
    "window": {"days": 30, "since": "2026-07-18T00:00:00Z"},
    "totals": {
      "requests": 12, "prompt_tokens": 3400, "output_tokens": 900,
      "est_cost_usd": 0.0031,
      "native": {"requests": 10, "prompt_tokens": 3000, "output_tokens": 800, "est_cost_usd": 0.0028},
      "byo":    {"requests": 2,  "prompt_tokens": 400,  "output_tokens": 100, "est_cost_usd": 0.0003}
    },
    "by_day": [
      {"day": "2026-08-16", "requests": 5, "prompt_tokens": 1200,
       "output_tokens": 300, "est_cost_usd": 0.0011}
    ],
    "by_model": [
      {"model": "gemini-3.6-flash", "is_native": true, "requests": 10,
       "prompt_tokens": 3000, "output_tokens": 800, "est_cost_usd": 0.0028}
    ]
  }
}

by_day is ascending by date and includes only days that had usage. Estimated cost is derived at read time from :data:_PRICE_PER_1M (so a price correction re-prices history) and rolled up per day from per-MODEL rates — NOT from an org-wide blended rate — so a day mixing Flash and Pro is priced correctly.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
days
integer (Days) [ 1 .. 365 ]
Default: 30

Window size in days (1..365). The range is [now - days, now].

Responses

Response samples

Content type
application/json
{ }

alert-rules

List Alert Rules

Authorizations:
HTTPBearer
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Alert Rule

Authorizations:
HTTPBearer
Request Body schema: application/json
required
required
object (AlertRuleCreate)
channel
required
string (Channel)
Enum: "create_incident" "slack" "email" "webhook" "pagerduty" "teams" "jira" "linear" "discord" "mattermost" "twilio_sms" "datadog" "jira_ticket" "servicenow" "github_issues" "opsgenie"
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
required
object (Destination)
enabled
boolean (Enabled)
Default: true
mentions
Array of strings (Mentions)
Default: []
min_severity
string (Min Severity)
Default: "critical"
Enum: "info" "warning" "critical"
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
scope_type
string (Scope Type)
Default: "workspace"
Enum: "specific" "all_validations" "workspace"
severities
Array of strings (Severities)
Items Enum: "info" "warning" "critical"
Team Id (string) or Team Id (null) (Team Id)
trigger
required
string (Trigger)
Enum: "on_failure" "on_success" "always" "on_anomaly"
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

List Workspace Alert Events

Authorizations:
HTTPBearer
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Delete Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Patch Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
Request Body schema: application/json
required
required
object (AlertRuleUpdate)
Channel (string) or Channel (null) (Channel)
Destination (object) or Destination (null) (Destination)
Enabled (boolean) or Enabled (null) (Enabled)
Array of Mentions (strings) or Mentions (null) (Mentions)
Min Severity (string) or Min Severity (null) (Min Severity)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Scope Type (string) or Scope Type (null) (Scope Type)
Array of Severities (strings) or Severities (null) (Severities)
Team Id (string) or Team Id (null) (Team Id)
Trigger (string) or Trigger (null) (Trigger)
Array of Validation Ids (strings) or Validation Ids (null) (Validation Ids)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Put Not Allowed

path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

List Alert Rule Events

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

List Alert Rules

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Alert Rule

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (AlertRuleCreate)
channel
required
string (Channel)
Enum: "create_incident" "slack" "email" "webhook" "pagerduty" "teams" "jira" "linear" "discord" "mattermost" "twilio_sms" "datadog" "jira_ticket" "servicenow" "github_issues" "opsgenie"
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
required
object (Destination)
enabled
boolean (Enabled)
Default: true
mentions
Array of strings (Mentions)
Default: []
min_severity
string (Min Severity)
Default: "critical"
Enum: "info" "warning" "critical"
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
scope_type
string (Scope Type)
Default: "workspace"
Enum: "specific" "all_validations" "workspace"
severities
Array of strings (Severities)
Items Enum: "info" "warning" "critical"
Team Id (string) or Team Id (null) (Team Id)
trigger
required
string (Trigger)
Enum: "on_failure" "on_success" "always" "on_anomaly"
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

List Workspace Alert Events

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Delete Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Patch Alert Rule

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (AlertRuleUpdate)
Channel (string) or Channel (null) (Channel)
Destination (object) or Destination (null) (Destination)
Enabled (boolean) or Enabled (null) (Enabled)
Array of Mentions (strings) or Mentions (null) (Mentions)
Min Severity (string) or Min Severity (null) (Min Severity)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Scope Type (string) or Scope Type (null) (Scope Type)
Array of Severities (strings) or Severities (null) (Severities)
Team Id (string) or Team Id (null) (Team Id)
Trigger (string) or Trigger (null) (Trigger)
Array of Validation Ids (strings) or Validation Ids (null) (Validation Ids)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Put Not Allowed

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

List Alert Rule Events

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

audit-events

List Audit Events

List audit events for one org, newest-first.

Owner + admin only — editor/viewer/agent tokens all get 403. Returns {"data": [...], "next_cursor": "..."}. next_cursor is null when the current page is the last one.

Issue #196: admin was previously denied here, but the per-resource history modal is wired into every detail page and admins are the primary day-to-day audience for "who changed this, when?". The role set matches the rest of the admin-readable surface (agent_tokens, llm_configs, the bulk AI routes, etc.) which all gate on ("admin", "owner").

FEATURE-8: pass resource_type=<kind>&resource_id=<uuid> to scope the list to a single resource (e.g. one datasource, one rule suite, one checkpoint). Both filters must be supplied together; one without the other is rejected as 400 — resource_type alone would scan a hot prefix of the index, and resource_id alone is ambiguous because UUIDs are not globally unique across resource types in this table.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
Resource Type (string) or Resource Type (null) (Resource Type)
Resource Id (string) or Resource Id (null) (Resource Id)

Responses

Response samples

Content type
application/json
null

audit-siem-webhooks

List Audit Siem Webhooks

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Audit Siem Webhook

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Event Filter (object) or Event Filter (null) (Event Filter)
name
required
string (Name) [ 1 .. 255 ] characters
Signing Secret (string) or Signing Secret (null) (Signing Secret)
url
required
string (Url)

Responses

Request samples

Content type
application/json
{
  • "event_filter": { },
  • "name": "string",
  • "signing_secret": "string",
  • "url": "string"
}

Response samples

Content type
application/json
null

Delete Audit Siem Webhook

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Audit Siem Webhook

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)

Responses

Response samples

Content type
application/json
null

Patch Audit Siem Webhook

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)
Request Body schema: application/json
required
Enabled (boolean) or Enabled (null) (Enabled)
Event Filter (object) or Event Filter (null) (Event Filter)
Name (string) or Name (null) (Name)
Signing Secret (string) or Signing Secret (null) (Signing Secret)
Url (string) or Url (null) (Url)

Responses

Request samples

Content type
application/json
{
  • "enabled": true,
  • "event_filter": { },
  • "name": "string",
  • "signing_secret": "string",
  • "url": "string"
}

Response samples

Content type
application/json
null

Clear Previous Audit Siem Webhook Secret

AUDIT-3.4: force-clear the previous-secret grace window.

Recovery path for the case where the operator successfully rotated but lost the new plaintext in flight (terminal closed before paste, CI step crashed mid-copy, browser hung). The 24h grace window guard on :func:rotate_audit_siem_webhook_secret would otherwise lock them out for a day, with a verifier-side secret nobody on the team knows.

Semantics:

  • Drops previous_secret_encrypted and previous_secret_expires_at to NULL. After this returns the rotate endpoint accepts a fresh call immediately.
  • Idempotent — calling it on a row that already has no previous secret is a no-op that still 204s and still emits an audit row. The audit footprint of "operator tried to clear" is the load-bearing signal here; collapsing the no-op case into a 404 would mask that signal.
  • Refuses if there is no current secret either — that's a different failure mode (the row's signing config is entirely empty) and the right fix is PATCH signing_secret=<value>, not this endpoint. We 422 with a directive detail so the UI can surface the actual next step instead of a generic error.
  • Audited as audit_siem_webhook.previous_secret_cleared so the compliance trail records the escape-hatch invocation distinctly from the rotate that preceded it. Receivers reading the audit feed see rotate -> previous_secret_cleared -> rotate and can reason about the sequence.
Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Rotate Audit Siem Webhook Secret

AUDIT-3.2: rotate the HMAC signing secret with a 24h grace window.

The flow:

  1. Move secret_encrypted (the current secret's ciphertext) to previous_secret_encrypted so the dispatcher can keep signing in-flight bodies with it during the grace window.
  2. Stamp previous_secret_expires_at = now() + 24h.
  3. Mint a fresh 32-byte URL-safe-b64 secret, encrypt it under the tenant DEK, and store the ciphertext as the new secret_encrypted.
  4. Return {secret, expires_old_at} so the operator can paste the new secret into their receiver and knows the cutoff after which the receiver MUST be verifying the new secret only.

Single-use guard: if a previous rotation is still inside its grace window, we refuse with 409. Without this, a double-rotation would silently lose the original secret and any receiver still verifying against it would start dropping events. Operators who really need to force a second rotation can wait out the window or PATCH the row to clear the previous-secret columns (an emergency lever — the docs warn against it).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)

Responses

Response samples

Content type
application/json
null

Fire Audit Siem Webhook Test

Enqueue a synthetic dispatch_audit_siem so the operator can verify their receiver wiring without waiting for a real audit event. The worker's job accepts event_id=None and builds a payload with action="audit_siem_webhook.test" so receivers can distinguish a probe from a real fire.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
webhook_id
required
string <uuid> (Webhook Id)

Responses

Response samples

Content type
application/json
null

bi

Org Bi Asset Status

Org-wide per-asset BI feed across every accessible workspace (#37).

Org-admin-tier ONLY (mirrors org_dashboard_metrics): a caller below super_admin / the legacy compat spellings gets a 403. No ?team_id= (team is a within-workspace concept). Isolation-respecting.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
Limit (integer) or Limit (null) (Limit)

Opt-in page size. Omit for the full row set in one response (next_cursor null); supply it to page via next_cursor.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Org Bi Runs

Org-wide per-run BI feed across every accessible workspace (#37).

Org-admin-tier ONLY (mirrors org_dashboard_metrics): a caller below super_admin / the legacy compat spellings gets a 403. No ?team_id= (team is a within-workspace concept). Isolation-respecting.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
Limit (integer) or Limit (null) (Limit)

Opt-in page size. Omit for the full window in one response (next_cursor null); supply it to page via next_cursor.

Since (string) or Since (null) (Since)

Inclusive run_time lower bound (ISO 8601). Naive timestamps are interpreted as UTC.

Until (string) or Until (null) (Until)

Inclusive run_time upper bound (ISO 8601). Naive timestamps are interpreted as UTC.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Workspace Bi Asset Status

Per-asset latest-result BI feed, workspace-scoped (#37).

One flat row per data asset — name, datasource, owner, tags, current passing/failing/never_run status, last run — for a BI tool to pull into a table. Viewer-readable (workspace_principal + CAN_VIEW_RESULTS); dqk_ personal keys and dqsa_ service accounts pass, agent / SCIM tokens are rejected. Isolation-respecting.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
Limit (integer) or Limit (null) (Limit)

Opt-in page size. Omit for the full row set in one response (next_cursor null); supply it to page via next_cursor.

Team Id (string) or Team Id (null) (Team Id)

Narrow the rows to one team's slice — must be a live team of this workspace, else 404. Assets carry no team, so a team filter narrows the results feeding each asset's status, not the asset set itself.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Workspace Bi Runs

Per-run BI fact-table feed, workspace-scoped (#37).

One flat row per validation run over an optional ?since=/?until= window, newest-first — the time-series table a BI tool trends. Run-level columns only in v1 (per-expectation pass-rate / severity are a follow-up). Viewer-readable; dqk_ / dqsa_ pass, agent / SCIM rejected. Isolation-respecting.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
Limit (integer) or Limit (null) (Limit)

Opt-in page size. Omit for the full window in one response (next_cursor null); supply it to page via next_cursor.

Since (string) or Since (null) (Since)

Inclusive run_time lower bound (ISO 8601). Naive timestamps are interpreted as UTC.

Until (string) or Until (null) (Until)

Inclusive run_time upper bound (ISO 8601). Naive timestamps are interpreted as UTC.

Team Id (string) or Team Id (null) (Team Id)

Narrow the runs to one team's slice — must be a live team of this workspace, else 404 (team ownership is derived via the parent checkpoint).

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

branding

Get Branding

Return the org's branding metadata + inline logo data URIs.

Readable by any org member. When no logo is set the variants are null and the UI falls back to the MeasuredCloud wordmark.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Reset Branding Logo

Reset the org logo to default by dropping the branding row. Org-admin gated. Idempotent — resetting an org with no logo is a 204 no-op.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Branding Logo

Stream the raw logo bytes for variant with its own content type.

Member-readable. 404 when the org has no row or the requested variant is unset (the caller then falls back to the wordmark / the other variant).

Every response carries X-Content-Type-Options: nosniff (the browser must honour the declared type, not sniff bytes) and an explicit Content-Disposition. For image/svg+xml we additionally lock the document down with a no-resources, sandboxed CSP so an SVG opened directly in the API origin can't execute embedded script — without that, an admin-uploaded <script> would run against the dqr_ cookie origin (stored XSS). Raster variants stay inline and render normally.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
variant
string (Variant)
Default: "light"
Enum: "light" "dark"

Responses

Response samples

Content type
application/json
null

Set Branding Logo

Set / replace the org logo. Org-admin gated.

Body carries base64 logo_light and/or logo_dark plus a content_type (with optional per-variant logo_light_type / logo_dark_type overrides). At least one variant is required. Each variant is validated for base64-decodability and the MAX_LOGO_BYTES size cap; every content type is validated against ALLOWED_CONTENT_TYPES.

Only the variant(s) this PUT addresses are touched — both the bytes and that variant's type move together, and the other variant's bytes/type are left intact (so a single-variant update can't desync the row).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
content_type
required
string (Content Type)
Logo Dark (string) or Logo Dark (null) (Logo Dark)
Logo Dark Type (string) or Logo Dark Type (null) (Logo Dark Type)
Logo Light (string) or Logo Light (null) (Logo Light)
Logo Light Type (string) or Logo Light Type (null) (Logo Light Type)

Responses

Request samples

Content type
application/json
{
  • "content_type": "string",
  • "logo_dark": "string",
  • "logo_dark_type": "string",
  • "logo_light": "string",
  • "logo_light_type": "string"
}

Response samples

Content type
application/json
null

catalog-integrations

List Integrations

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Integration

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Auth Token (string) or Auth Token (null) (Auth Token)
enabled
boolean (Enabled)
Default: true
endpoint
required
string (Endpoint)
kind
required
string (Kind)

Responses

Request samples

Content type
application/json
{
  • "auth_token": "string",
  • "enabled": true,
  • "endpoint": "string",
  • "kind": "string"
}

Response samples

Content type
application/json
null

Delete Integration

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
integration_id
required
string <uuid> (Integration Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Patch Integration

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
integration_id
required
string <uuid> (Integration Id)
Request Body schema: application/json
required
Auth Token (string) or Auth Token (null) (Auth Token)
Enabled (boolean) or Enabled (null) (Enabled)
Endpoint (string) or Endpoint (null) (Endpoint)

Responses

Request samples

Content type
application/json
{
  • "auth_token": "string",
  • "enabled": true,
  • "endpoint": "string"
}

Response samples

Content type
application/json
null

Trigger Sync

Enqueue an out-of-cycle inbound walk for this one integration.

PR-#102 review blocker: the original cut enqueued the cron entry-point (catalog_sync_inbound), which walked every enabled integration across every org — at best wasteful, at worst a cross-tenant correctness bug (an operator hitting "Sync now" for org A bumped last_synced_at on org B's rows). We now enqueue the per-integration job so the walk is scoped to (org_id, integration_id).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
integration_id
required
string <uuid> (Integration Id)

Responses

Response samples

Content type
application/json
null

catalog-sync

Get Catalog Sync Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Patch Catalog Sync Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Signing Secret (string) or Signing Secret (null) (Signing Secret)
Webhook Url (string) or Webhook Url (null) (Webhook Url)

Responses

Request samples

Content type
application/json
{
  • "signing_secret": "string",
  • "webhook_url": "string"
}

Response samples

Content type
application/json
null

Fire Catalog Sync Test

Enqueue a synthetic notify_catalog_sync fire so the operator can verify the receiver is wired up correctly without waiting for a real checkpoint to land.

Returns 202 — the worker handles the POST asynchronously. The operator polls last_fired_at (or watches their receiver) to observe arrival.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

checkpoints (deprecated)

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

List Checkpoints Deprecated

DEPRECATED: the legacy checkpoints, each with its mapped validation_id.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Get Checkpoint Deprecated

DEPRECATED: one legacy checkpoint with its mapped validation_id.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)

Responses

Response samples

Content type
application/json
{ }

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

List Checkpoints Ws Deprecated

DEPRECATED: the legacy checkpoints, each with its mapped validation_id.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Get Checkpoint Ws Deprecated

DEPRECATED: one legacy checkpoint with its mapped validation_id.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)

Responses

Response samples

Content type
application/json
{ }

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Gone: checkpoints were retired (use validations) Deprecated

Responses

Response samples

Content type
application/json
null

Run Checkpoint Now Deprecated

DEPRECATED: runs the checkpoint's mapped validation (an execution run).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)
Request Body schema: application/json
Any of
Rule Parameters (object) or Rule Parameters (null) (Rule Parameters)

No longer supported: a validation run executes its rules as saved. A non-empty value is rejected (422).

Any of
property name*
additional property
any

Responses

Request samples

Content type
application/json
Example
{
  • "rule_parameters": { }
}

Response samples

Content type
application/json
{ }

Run Checkpoint Subset Deprecated

DEPRECATED: runs the mapped validation once per partition value.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)
Request Body schema: application/json
required
batch_definition_id
required
string <uuid> (Batch Definition Id)
required
Array of strings or integers (Partition Values) [ 1 .. 100 ] items

Responses

Request samples

Content type
application/json
{
  • "batch_definition_id": "a6461f10-95ac-4c7c-88ae-d2162c5ff75a",
  • "partition_values": [
    ]
}

Response samples

Content type
application/json
{ }

data-api

Rule Parameters

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)

Responses

Response samples

Content type
application/json
null

Delete By Name

Authorizations:
HTTPBearer
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Resources

Authorizations:
HTTPBearer
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Resource

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Delete By Id

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Patch Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Put Not Allowed

path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Delete By Name

Authorizations:
HTTPBearer
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Resources

Authorizations:
HTTPBearer
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Resource

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Delete By Id

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Patch Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Update Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Create Metric Run

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

List Validation Results

Authorizations:
HTTPBearer
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Parent Result Id (string) or Parent Result Id (null) (Parent Result Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)
Limit (integer) or Limit (null) (Limit)
offset
integer (Offset) >= 0
Default: 0
Sort (string) or Sort (null) (Sort)
Asset (string) or Asset (null) (Asset)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)

Responses

Response samples

Content type
application/json
null

Export Validation Results Csv

Authorizations:
HTTPBearer
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)
Asset (string) or Asset (null) (Asset)
format
string (Format) ^(csv|xlsx)$
Default: "csv"

Responses

Response samples

Content type
application/json
null

Validation Results Summary

Authorizations:
HTTPBearer
query Parameters
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)

Responses

Response samples

Content type
application/json
null

Get Validation Result

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Rule Parameters Ws

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)

Responses

Response samples

Content type
application/json
null

Delete By Name

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Resources

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Resource

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Delete By Id

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Patch Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Put Not Allowed

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Delete By Name

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Resources

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Resource

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Delete By Id

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Patch Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Update Resource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Create Metric Run Ws

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

List Validation Results

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Parent Result Id (string) or Parent Result Id (null) (Parent Result Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)
Limit (integer) or Limit (null) (Limit)
offset
integer (Offset) >= 0
Default: 0
Sort (string) or Sort (null) (Sort)
Asset (string) or Asset (null) (Asset)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)

Responses

Response samples

Content type
application/json
null

Export Validation Results Csv

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Tag (string) or Tag (null) (Tag)
Dbt Model (string) or Dbt Model (null) (Dbt Model)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)
Asset (string) or Asset (null) (Asset)
format
string (Format) ^(csv|xlsx)$
Default: "csv"

Responses

Response samples

Content type
application/json
null

Validation Results Summary

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Status (string) or Status (null) (Status)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)

Responses

Response samples

Content type
application/json
null

Get Validation Result

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Delete Datasource By Name

Authorizations:
HTTPBearer
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Datasources

Authorizations:
HTTPBearer
query Parameters
Name (string) or Name (null) (Name)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Datasource

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Delete Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Patch Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Update Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Recycle Bin

Authorizations:
HTTPBearer
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Purge Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Restore Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Purge Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Restore Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Preview Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Preview Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Delete Datasource By Name

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
name
required
string (Name)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Datasources

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Datasource

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Delete Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Patch Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Update Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Recycle Bin

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Purge Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Restore Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Purge Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Restore Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Preview Data Asset

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Preview Datasource

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Accounts Me

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

custom-expectations

List Custom Expectations

List registered custom expectations for the calling principal's org.

Open to every authenticated member of the org — the rule-suite editor is the primary consumer and editor-level users need to see the catalog. Owner-only on this surface would block the editor for every non-owner member.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

List Plugin Packages

List configured plugin packages + import status.

Open to every authenticated member of the org (#113). The Plugins settings page renders the response for any member who lands on it; the previous owner-only gate broke the page for editors/viewers even though the package names are deployment config (already visible in the worker env) and carry no secrets. Each package is imported in this process so an operator who pasted a typo into EXPECTATIONS_PLUGIN_PACKAGES sees the failure in the settings UI without having to tail the worker logs.

The response carries installed=True for packages that import cleanly and error=<message> for ones that don't. The import is cached by Python's import machinery so repeated reads are cheap.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

dashboard

Org Asset Health

Org-wide asset-health roll-up across every accessible workspace (#142).

Org-admin-tier ONLY — mirrors org_dashboard_metrics / results_all's org endpoint exactly: a caller below super_admin (or the legacy owner / admin compat spellings) gets a 403, NOT a partial roll-up. An org-admin-tier role yields accessible = None from :func:derive_accessible_workspaces, so the listener emits a tautology and the SAME :func:_compute_asset_health SQL rolls up every workspace's assets. No path {workspace_id} means the workspace clamp never fires. No ?team_id= here — team is a within-workspace concept.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Org Dashboard Metrics

Org-wide dashboard roll-up across every accessible workspace (#665).

Org-admin-tier ONLY — mirrors results_all's org endpoint exactly: a caller below super_admin (or the legacy owner / admin compat spellings) gets a 403, NOT a partial roll-up. An org-admin-tier role yields accessible = None from :func:derive_accessible_workspaces, so the listener emits a tautology and the SAME :func:_compute_metrics SQL rolls up every workspace's rows in the org's tenant schema. No path {workspace_id} means the #646 workspace clamp never fires, so the numbers genuinely span the whole org rather than one workspace.

No ?team_id= here — team is a within-workspace concept; the org tier is intentionally the union across workspaces.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
period
integer (Period)
Default: 30

Window in days — one of 30, 60, 90, 365 (the 30/60/90/1y selector).

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Org Dashboard Metrics Export

Download the org-wide dashboard roll-up as .xlsx (#130 PR 2).

Gated IDENTICALLY to :func:org_dashboard_metrics — org-admin-tier ONLY (a caller below super_admin / the legacy compat spellings gets a 403, NOT a partial roll-up), same rate bucket, same _compute_metrics through the isolation listener. No {workspace_id} in the path, so the roll-up spans every accessible workspace. File attachment, not the {"data": ...} envelope.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
format
string (Format) ^(xlsx)$
Default: "xlsx"
period
integer (Period)
Default: 30

Window in days — one of 30, 60, 90, 365 (the 30/60/90/1y selector).

Responses

Response samples

Content type
application/json
null

Workspace Asset Health

Workspace- (or team-) scoped asset-health roll-up (#142).

Viewer-readable: workspace_principal (org match + workspace membership; org admins bypass per HARDEN-17) + CAN_VIEW_RESULTS. The listener filters every query to the caller's accessible workspaces, clamped to the path workspace on this workspace-scoped read.

?team_id= narrows the failing-side counts via the listener's team predicate (see :func:dashboard_metrics._narrow_to_team); the asset totals stay workspace-level (assets carry no team) and team_narrowed is False.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Team Id (string) or Team Id (null) (Team Id)

Narrow the FAILING-side counts (good/failing/by_owner) to one team's slice (#142). Must be a live team of this workspace, else 404. total_assets/never_run stay workspace-level (assets carry no team); team_narrowed is then False. Omit for the whole-workspace numbers.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Dashboard Metrics

Workspace- (or team-) scoped dashboard metrics for one period (#657/#665).

Viewer-readable: workspace_principal (org match + workspace membership; org admins bypass per HARDEN-17) + CAN_VIEW_RESULTS (which the viewer role holds). The listener filters every query to the caller's accessible workspaces, clamped to the path workspace on this workspace-scoped read.

?team_id= further narrows to one team via the listener's team predicate (see :func:_narrow_to_team); coverage.total stays workspace-level because data assets carry no team (documented in the module docstring).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
period
integer (Period)
Default: 30

Window in days — one of 30, 60, 90, 365 (the 30/60/90/1y selector).

Team Id (string) or Team Id (null) (Team Id)

Narrow the metrics to one team's slice (#665). Must be a live team of this workspace, else 404. Omit for the whole-workspace numbers.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Dashboard Metrics Export

Download the workspace- (or team-) scoped dashboard metrics as .xlsx (#130 PR 2).

Gated IDENTICALLY to :func:dashboard_metrics (workspace_principal + CAN_VIEW_RESULTS, same rate bucket, same period/team validation and the same _compute_metrics through the isolation listener), so a user can export exactly the numbers they can see — no more, no less. ?format= is xlsx (the only container; a typo 422s rather than silently returning something else). The response is a file attachment, NOT the {"data": ...} envelope (file downloads are the documented exception, cf. the results export).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
format
string (Format) ^(xlsx)$
Default: "xlsx"
period
integer (Period)
Default: 30

Window in days — one of 30, 60, 90, 365 (the 30/60/90/1y selector).

Team Id (string) or Team Id (null) (Team Id)

Narrow the metrics to one team's slice (#665). Must be a live team of this workspace, else 404. Omit for the whole-workspace numbers.

Responses

Response samples

Content type
application/json
null

asset-rules

Import Gx Suite

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (GxSuiteImport)

Body for POST .../data-assets/{asset_id}/rule-suites/import (inside the data envelope) — an existing Great Expectations ExpectationSuite.

The "coming from GX" on-ramp: the platform is a thin 1:1 wrapper over GX expectation_type + kwargs, so an exported GX suite maps in directly. We are LIBERAL in what we accept (every GX shape that has shipped):

  • name — GX 1.x. expectation_suite_name — GX 0.x. Either is accepted as the suite name; both optional (a bare expectation list has neither, so the import falls back to the asset's name).
  • expectations — the list of expectation dicts. Each entry's type is read from type (GX 1.x) OR expectation_type (GX 0.x); kwargs and an optional meta carry through. Extra top-level keys (meta, ge_cloud_id, …) are ignored.

The route normalises the suite via dq_shared.expectations.normalise_gx_import into a SINGLE editor-rules channel and stores that (see the router) — every expectation runs (unknown types hydrate through unchanged; a GX UnexpectedRowsExpectation becomes a validated custom-SQL rule). The per-expectation dicts are intentionally typed as open dict here rather than re-modelled — the normaliser is the single source of truth for the accepted shape, and modelling GX's full expectation schema here would only drift from it (mirrors why AssetRulesWrite.rules is an open dict).

model_config allows a BARE list of expectation dicts too — but Pydantic can't model "either an object or a list" on one class, so the route accepts the bare-list shape by pre-parsing the raw body before constructing this model (this class describes the common object shape for the OpenAPI schema).

Expectation Suite Name (string) or Expectation Suite Name (null) (Expectation Suite Name)
Array of objects (Expectations)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Import Gx Suite

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (GxSuiteImport)

Body for POST .../data-assets/{asset_id}/rule-suites/import (inside the data envelope) — an existing Great Expectations ExpectationSuite.

The "coming from GX" on-ramp: the platform is a thin 1:1 wrapper over GX expectation_type + kwargs, so an exported GX suite maps in directly. We are LIBERAL in what we accept (every GX shape that has shipped):

  • name — GX 1.x. expectation_suite_name — GX 0.x. Either is accepted as the suite name; both optional (a bare expectation list has neither, so the import falls back to the asset's name).
  • expectations — the list of expectation dicts. Each entry's type is read from type (GX 1.x) OR expectation_type (GX 0.x); kwargs and an optional meta carry through. Extra top-level keys (meta, ge_cloud_id, …) are ignored.

The route normalises the suite via dq_shared.expectations.normalise_gx_import into a SINGLE editor-rules channel and stores that (see the router) — every expectation runs (unknown types hydrate through unchanged; a GX UnexpectedRowsExpectation becomes a validated custom-SQL rule). The per-expectation dicts are intentionally typed as open dict here rather than re-modelled — the normaliser is the single source of truth for the accepted shape, and modelling GX's full expectation schema here would only drift from it (mirrors why AssetRulesWrite.rules is an open dict).

model_config allows a BARE list of expectation dicts too — but Pydantic can't model "either an object or a list" on one class, so the route accepts the bare-list shape by pre-parsing the raw body before constructing this model (this class describes the common object shape for the OpenAPI schema).

Expectation Suite Name (string) or Expectation Suite Name (null) (Expectation Suite Name)
Array of objects (Expectations)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Delete Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{ }

Post Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
AssetRuleCreate (object) or AssetRulesWrite (object) (Data)
One of
NewValidation (object) or null
Description (string) or Description (null) (Description)
object (Kwargs)
Name (string) or Name (null) (Name)
Position (integer) or Position (null) (Position)
rule_type
required
string (Rule Type) non-empty
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Put Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (AssetRulesWrite)

Body for PUT / POST .../data-assets/{asset_id}/rules (inside the data envelope) — the full rule list to persist on the asset's suite.

Mirrors the {"rules": [...]} shape the suite editor / worker / SDK all speak. Each rule is a free-form {"rule_type", "kwargs"} object; the route runs the list through validate_rule_suite_payload before it touches the DB (a malformed custom-SQL rule → 422), so the rule entries are intentionally typed as open dict here rather than re-modelled (the validator is the single source of truth — no duplicate schema to drift).

PUT and POST share this body: both replace the asset's rule list. An entry that carries the id of one of the asset's rules updates that rule in place (its id, validation memberships and — unless what it checks changed — its memory are kept); an entry without one is matched by canonical identity, else created. Rules missing from the list are deleted. Prefer the per-rule POST / PATCH / DELETE endpoints for single edits.

EPIC PR 4 (additive): an entry may also carry validation_ids (curated validations the rule should join, beside its asset's default) and create_validation ({"name": ...}, a new validation to create around it; entries naming the same new validation share one). Both are applied in the same transaction as the rule write, so a bad validation id rolls the whole write back. They only ever add memberships, and need the manage-validation permission.

Array of objects (Rules)
Array
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Apply Template To Asset

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (ApplyTemplateRequest)

Body for POST .../data-assets/{asset_id}/rules/apply-template (inside the data envelope).

Applies a reusable, asset-agnostic RuleTemplate to THIS asset. The template's rules are MERGED into the asset's own suite (append + de-dupe on (rule_type, kwargs)) — never replacing hand-authored rules, so the #497 per-line save model is preserved.

column_map remaps a template rule's target column to this asset's column name ({"<template column>": "<asset column>"}) — the "map them to the relevant tables" step when the template's columns don't all match. A column not present in the map passes through unchanged. dry_run computes and returns the would-be merged rule list WITHOUT persisting (drives the preview / column-map dialog).

object (Column Map)
dry_run
boolean (Dry Run)
Default: false
template_id
required
string <uuid> (Template Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Run Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Asset Running Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{ }

Delete Asset Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Patch Asset Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (AssetRulePatch)

Body for PATCH .../data-assets/{asset_id}/rules/{rule_id} (inside the data envelope) — edit ONE rule in place. Every field is optional; an omitted field is left alone. The rule keeps its id, so its validation memberships survive. Changing what the rule checks (rule_type or any kwarg other than severity) clears its rule memory (#142); a severity-only change keeps it. kwargs replaces the whole kwargs object. An explicit null name / description clears it.

Description (string) or Description (null) (Description)
Kwargs (object) or Kwargs (null) (Kwargs)
Name (string) or Name (null) (Name)
Rule Type (string) or Rule Type (null) (Rule Type)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Set Rule Memory

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (RuleMemoryWrite)

Body for PUT .../data-assets/{asset_id}/rules/{rule_id}/memory — set (or clear) a rule's memory. flag NULL clears the flag; note NULL/empty clears the note. Bounded so a runaway note can't bloat the row.

Flag (string) or Flag (null) (Flag)
Note (string) or Note (null) (Note)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Draft Rule Memory

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Run Single Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Delete Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Post Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
AssetRuleCreate (object) or AssetRulesWrite (object) (Data)
One of
NewValidation (object) or null
Description (string) or Description (null) (Description)
object (Kwargs)
Name (string) or Name (null) (Name)
Position (integer) or Position (null) (Position)
rule_type
required
string (Rule Type) non-empty
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Put Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (AssetRulesWrite)

Body for PUT / POST .../data-assets/{asset_id}/rules (inside the data envelope) — the full rule list to persist on the asset's suite.

Mirrors the {"rules": [...]} shape the suite editor / worker / SDK all speak. Each rule is a free-form {"rule_type", "kwargs"} object; the route runs the list through validate_rule_suite_payload before it touches the DB (a malformed custom-SQL rule → 422), so the rule entries are intentionally typed as open dict here rather than re-modelled (the validator is the single source of truth — no duplicate schema to drift).

PUT and POST share this body: both replace the asset's rule list. An entry that carries the id of one of the asset's rules updates that rule in place (its id, validation memberships and — unless what it checks changed — its memory are kept); an entry without one is matched by canonical identity, else created. Rules missing from the list are deleted. Prefer the per-rule POST / PATCH / DELETE endpoints for single edits.

EPIC PR 4 (additive): an entry may also carry validation_ids (curated validations the rule should join, beside its asset's default) and create_validation ({"name": ...}, a new validation to create around it; entries naming the same new validation share one). Both are applied in the same transaction as the rule write, so a bad validation id rolls the whole write back. They only ever add memberships, and need the manage-validation permission.

Array of objects (Rules)
Array
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Apply Template To Asset

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (ApplyTemplateRequest)

Body for POST .../data-assets/{asset_id}/rules/apply-template (inside the data envelope).

Applies a reusable, asset-agnostic RuleTemplate to THIS asset. The template's rules are MERGED into the asset's own suite (append + de-dupe on (rule_type, kwargs)) — never replacing hand-authored rules, so the #497 per-line save model is preserved.

column_map remaps a template rule's target column to this asset's column name ({"<template column>": "<asset column>"}) — the "map them to the relevant tables" step when the template's columns don't all match. A column not present in the map passes through unchanged. dry_run computes and returns the would-be merged rule list WITHOUT persisting (drives the preview / column-map dialog).

object (Column Map)
dry_run
boolean (Dry Run)
Default: false
template_id
required
string <uuid> (Template Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Run Asset Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Asset Running Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Delete Asset Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Patch Asset Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (AssetRulePatch)

Body for PATCH .../data-assets/{asset_id}/rules/{rule_id} (inside the data envelope) — edit ONE rule in place. Every field is optional; an omitted field is left alone. The rule keeps its id, so its validation memberships survive. Changing what the rule checks (rule_type or any kwarg other than severity) clears its rule memory (#142); a severity-only change keeps it. kwargs replaces the whole kwargs object. An explicit null name / description clears it.

Description (string) or Description (null) (Description)
Kwargs (object) or Kwargs (null) (Kwargs)
Name (string) or Name (null) (Name)
Rule Type (string) or Rule Type (null) (Rule Type)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Set Rule Memory

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleMemoryWrite)

Body for PUT .../data-assets/{asset_id}/rules/{rule_id}/memory — set (or clear) a rule's memory. flag NULL clears the flag; note NULL/empty clears the note. Bounded so a runaway note can't bloat the row.

Flag (string) or Flag (null) (Flag)
Note (string) or Note (null) (Note)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Draft Rule Memory

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Run Single Rule

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

data-docs

Get Data Docs Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Patch Data Docs Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Destination (string) or Destination (null) (Destination)
Any of
string (Destination)

Responses

Request samples

Content type
application/json
{
  • "destination": "string"
}

Response samples

Content type
application/json
null

Rebuild Data Docs

Enqueue an out-of-cycle build for this org.

The job lives in worker/jobs/build_data_docs.py and is registered as a regular ARQ function (not only a cron) precisely so the API can trigger it on demand. Returns 202 — the work happens asynchronously and the caller polls last_built_at to observe completion.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

dbt-webhook

Receive Dbt Webhook

Inbound dbt Cloud webhook receiver.

Flow:

  1. Load the org's signing secret. NULL → 404 (we treat the endpoint as not-configured-for-this-org rather than 401 because leaking "this org exists but isn't configured" is worse signal to a probing attacker than a flat not-found).
  2. Read the raw body bytes (NOT the parsed JSON — see HMAC notes above) and verify the Authorization: Token <hex> header.
  3. Filter for production runs and successful model nodes.
  4. Look up validations tagged dbt:<schema>.<model> for each model in the payload.
  5. Start one execution run per matched validation, mirroring a manual run (services.validation_runs).
  6. Stamp dbt_webhook_last_received_at so the UI can show feed health.

Authorization: no get_principal dependency — the HMAC signature IS the authorization. dbt Cloud isn't an org member; it carries a shared secret.

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{ }

Get Dbt Webhook Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Patch Dbt Webhook Config

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Signing Secret (string) or Signing Secret (null) (Signing Secret)
Any of
string (Signing Secret)

Responses

Request samples

Content type
application/json
{
  • "signing_secret": "string"
}

Response samples

Content type
application/json
null

workspaces

List Deleted Workspaces

The deleted (archived) workspaces the caller may see, newest first.

A super admin sees every one in the org (they can restore them). Any other user sees the ones they belonged to — directly, or through a team the delete retired — so the Settings card can show them with Restore disabled rather than hiding them. Non-user tokens see none. Nothing here opens the workspace: it stays 404 on every workspace-scoped route until restored.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List Workspaces

List the workspaces the calling principal can access (#446, #382).

Powers the UI's workspace switcher. Wire shape::

{ "data": [ { "id": "...", "name": "...", "slug": "...",
              "role": "super_admin|technical_admin|workspace_admin|..." } ] }

Visibility mirrors the TENANCY-1 accessible-workspace resolution (see :func:db.workspace_isolation.derive_accessible_workspaces and :func:auth.dependencies.workspace_principal):

  • Org super admin — their effective role spans the whole org, so they see every workspace in the org. role echoes their org role (super_admin), matching the workspace_principal bypass that hands org admins the org role for any workspace.
  • Technical admin (#321) — every workspace too, tagged technical_admin: they navigate the org tree to manage each workspace's data-source connections (and nothing else).
  • Everyone else (user-token editors / viewers) — only the workspaces they can reach via a direct workspace_memberships row or via a team they belong to whose workspace_id is that workspace (Phase-1 Unit 3). role is the effective (highest) role across those paths, the same merge :func:auth.effective_role.resolve_workspace_role applies per-workspace.
  • Non-user tokens (dqk_ / dqa_) without org owner / admin authority carry no workspace membership, so they see an empty list — consistent with workspace_principal rejecting them. (An org-wide programmatic token still authenticates; it just has no membership rows to surface here.)

Any authenticated org member may call this; org_id must match the principal's org (403 otherwise). The list is empty rather than 404 when the caller has no accessible workspaces.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Workspace

Create a new workspace under org_id.

Returns the new workspace shape (id / name / slug / org_id / timestamps). A slug collision against another workspace in the same org returns 409 (the (org_id, slug) unique constraint backstops the race). The QUOTA-1 gate (see dependencies=) rejects creates that would exceed the org's plan max_workspaces with HTTP 402 + structured quota_exceeded body.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
name
required
string (Name) [ 1 .. 255 ] characters
slug
required
string (Slug) [ 2 .. 100 ] characters

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
null

Delete Workspace

Delete (archive) a workspace. See the policy block above.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
confirm_name
required
string (Confirm Name)

The workspace's exact name, typed back.

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Workspace

Fetch a workspace's display fields (name, slug, timestamps).

Any org member can read — restricting reads to workspace-admins would make the Settings landing card unrenderable for editors / viewers, who are the bulk of users.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Update Workspace

Rename and/or re-slug a workspace.

Wire shape:

.. code-block:: json

{ "name": "Analytics — Prod", "slug": "analytics-prod" }

Either or both fields may be present. Sending neither is 422.

Authorization is owner / workspace_admin (see :func:_require_workspace_admin). A slug collision against another workspace in the same org returns 409 (the (org_id, slug) unique constraint backstops the race). Step-up auth is intentionally NOT required: a rename is reversible and doesn't bridge credentials — unlike invite-create / IdP CRUD which do gate on fresh_auth.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Name (string) or Name (null) (Name)
Slug (string) or Slug (null) (Slug)

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
null

Workspace Deletion Preview

#279: the inventory the Danger-zone dialog lists before a delete, plus is_last — true when this is the org's only live workspace, so the dialog can warn that the org will be left without one (#432: it no longer blocks).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Restore Workspace

Restore a deleted workspace. See the policy block above.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

identity-providers

List Identity Providers

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Identity Provider

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (Config)
default_role
string (Default Role)
Default: "viewer"
Enum: "super_admin" "technical_admin" "editor" "viewer"
enforced
boolean (Enforced)
Default: false
jit_provisioning_enabled
boolean (Jit Provisioning Enabled)
Default: false
Jit Team Prefix (string) or Jit Team Prefix (null) (Jit Team Prefix)
kind
required
string (Kind)
Enum: "oidc" "saml"
name
required
string (Name)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "default_role": "super_admin",
  • "enforced": false,
  • "jit_provisioning_enabled": false,
  • "jit_team_prefix": "string",
  • "kind": "oidc",
  • "name": "string"
}

Response samples

Content type
application/json
null

Discover Oidc Identity Provider

SSO-2: preview an OIDC IdP's discovery document by issuer URL.

Owners and admins paste the IdP's issuer URL (e.g. https://your-tenant.okta.com) into the "Add identity provider" modal and click Discover. The UI POSTs here, we GET {issuer}/.well-known/openid-configuration server-side, and return the trimmed fields the form needs. The endpoint is read-only: no IdP row is created or modified.

Two layers of SSRF protection apply (defence in depth, per the SSO-2 spec):

  1. :func:_validate_issuer_url enforces https://, parses out a hostname, runs :func:validate_destination_host to reject RFC-1918, loopback, link-local, and cloud-metadata addresses before any socket is opened. Any failure here surfaces as 400 with a sanitised reason — the raw exception is never propagated to the client because it could embed an internal hostname / port.
  2. :func:~dq_cloud_api.auth.sso.fetch_discovery then runs :func:~dq_cloud_api.auth.sso._ssrf_guard on both the issuer URL and every URL returned in the discovery document (token_endpoint, jwks_uri). That guard catches the case where the IdP is publicly resolvable but tries to direct us at an internal endpoint in its discovery response.

On upstream failure (non-200 from the IdP, malformed JSON, missing required field), fetch_discovery raises HTTPException(502) — we catch that and re-raise as 400 so the UI can render it inline next to the issuer field rather than as a "Bad Gateway" toast.

The response shape carries the four endpoints the create form needs (authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri) plus two informational arrays (scopes_supported, claims_supported) the UI shows as chips so the admin can confirm the IdP supports what they expect.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
issuer_url
required
string (Issuer Url)

Responses

Request samples

Content type
application/json
{
  • "issuer_url": "string"
}

Response samples

Content type
application/json
null

Delete Identity Provider

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
idp_id
required
string <uuid> (Idp Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Identity Provider

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
idp_id
required
string <uuid> (Idp Id)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Default Role (string) or Default Role (null) (Default Role)
Enforced (boolean) or Enforced (null) (Enforced)
Jit Provisioning Enabled (boolean) or Jit Provisioning Enabled (null) (Jit Provisioning Enabled)
Jit Team Prefix (string) or Jit Team Prefix (null) (Jit Team Prefix)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "default_role": "super_admin",
  • "enforced": true,
  • "jit_provisioning_enabled": true,
  • "jit_team_prefix": "string",
  • "name": "string"
}

Response samples

Content type
application/json
null

Link My Account

Review of PR #391: EXPLICITLY link the signed-in user's OWN account to one of their org's identity providers.

The SSO callback never AUTO-links an IdP identity by email to an org-admin-tier account (super_admin / technical_admin): whoever configures an IdP controls the email it asserts. An admin who wants to sign in with SSO proves their password here first (fresh_auth step-up); the returned redirect_url carries a one-shot state naming THEIR user id, and the callback links the IdP identity to that account only. Any member may use it for themselves; interactive user sessions only.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
idp_id
required
string <uuid> (Idp Id)

Responses

Response samples

Content type
application/json
null

incidents

List Incidents

Authorizations:
HTTPBearer
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Incident

Authorizations:
HTTPBearer
Request Body schema: application/json
required
required
object (IncidentCreate)
Assignee Email (string) or Assignee Email (null) (Assignee Email)
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Root Cause (string) or Root Cause (null) (Root Cause)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Rule Id (string) or Rule Id (null) (Rule Id)
severity
string (Severity)
Default: "critical"
Enum: "info" "warning" "critical"
title
required
string (Title)
Validation Result Id (string) or Validation Result Id (null) (Validation Result Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

List Assignable Members

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

List Rotations

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Put Rotation

#156 PR-2: upsert the rotation for a (scope, scope_ref_id).

Validates scope/ref coherence, that the scope target is VISIBLE in this workspace (team belongs to this workspace; asset resolves through the isolation-filtered DataAsset select — invisible → 404, same clamp posture as create_incident), and that every member is an assignable member of the workspace (non-member → 422, reusing PR-1's validator). The round-robin cursor is preserved by user-id across an edit when the previous next-assignee is still in the list, else clamped to 0 (product fork FE).

Authorizations:
HTTPBearer
Request Body schema: application/json
required
required
object (IncidentRotationWrite)

#156 PR-2: upsert body for a rotation config (keyed on scope + ref).

scope_ref_id is REQUIRED for team / asset (the target team / data-asset id) and MUST be null for workspace. member_user_ids is the ORDERED list of triage owners; the server validates every id is an assignable member of the target workspace (reusing PR-1's _assignable_members) and resets the round-robin cursor on write.

member_user_ids
Array of strings <uuid> (Member User Ids) [ items <uuid > ]
Default: []
scope
required
string (Scope)
Enum: "workspace" "team" "asset"
Scope Ref Id (string) or Scope Ref Id (null) (Scope Ref Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Incident Summary

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Team Id (string) or Team Id (null) (Team Id)

Narrow the summary to one team's slice (#141). Workspace prefix only — must be a live team of this workspace, else 404. Omit for the whole-workspace (or, on the org prefix, org-wide) numbers.

Responses

Response samples

Content type
application/json
null

Delete Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Patch Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
Request Body schema: application/json
required
required
object (IncidentUpdate)
Assignee Email (string) or Assignee Email (null) (Assignee Email)
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Resolution Note (string) or Resolution Note (null) (Resolution Note)
Root Cause (string) or Root Cause (null) (Root Cause)
Rule Id (string) or Rule Id (null) (Rule Id)
Severity (string) or Severity (null) (Severity)
Status (string) or Status (null) (Status)
Title (string) or Title (null) (Title)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Put Not Allowed

path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Assign Next

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

List Incident Events

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Create Incident Event

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
Request Body schema: application/json
required
required
object (_IncidentEventPayload)
body
required
string (Body)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Export Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

Push External Ticket

#155: push this incident to an external ServiceNow / PagerDuty ticket.

Reuses one of the workspace's EXISTING ServiceNow / PagerDuty alert-rule destinations (its stored, encrypted creds) to open one upstream ticket, stamps the external_ticket_* columns on the incident, and appends an external_ticket timeline entry. Push-only: there is no sync-back — a second push is refused (409) so an incident links at most one ticket.

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
Request Body schema: application/json
required
required
object (_ExternalTicketPayload)
alert_rule_id
required
string <uuid> (Alert Rule Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Peek Next Assignee

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
null

List Incidents

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Incident

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (IncidentCreate)
Assignee Email (string) or Assignee Email (null) (Assignee Email)
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Root Cause (string) or Root Cause (null) (Root Cause)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Rule Id (string) or Rule Id (null) (Rule Id)
severity
string (Severity)
Default: "critical"
Enum: "info" "warning" "critical"
title
required
string (Title)
Validation Result Id (string) or Validation Result Id (null) (Validation Result Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

List Assignable Members

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

List Rotations

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Put Rotation

#156 PR-2: upsert the rotation for a (scope, scope_ref_id).

Validates scope/ref coherence, that the scope target is VISIBLE in this workspace (team belongs to this workspace; asset resolves through the isolation-filtered DataAsset select — invisible → 404, same clamp posture as create_incident), and that every member is an assignable member of the workspace (non-member → 422, reusing PR-1's validator). The round-robin cursor is preserved by user-id across an edit when the previous next-assignee is still in the list, else clamped to 0 (product fork FE).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (IncidentRotationWrite)

#156 PR-2: upsert body for a rotation config (keyed on scope + ref).

scope_ref_id is REQUIRED for team / asset (the target team / data-asset id) and MUST be null for workspace. member_user_ids is the ORDERED list of triage owners; the server validates every id is an assignable member of the target workspace (reusing PR-1's _assignable_members) and resets the round-robin cursor on write.

member_user_ids
Array of strings <uuid> (Member User Ids) [ items <uuid > ]
Default: []
scope
required
string (Scope)
Enum: "workspace" "team" "asset"
Scope Ref Id (string) or Scope Ref Id (null) (Scope Ref Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Incident Summary

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Team Id (string) or Team Id (null) (Team Id)

Narrow the summary to one team's slice (#141). Workspace prefix only — must be a live team of this workspace, else 404. Omit for the whole-workspace (or, on the org prefix, org-wide) numbers.

Responses

Response samples

Content type
application/json
null

Delete Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Patch Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (IncidentUpdate)
Assignee Email (string) or Assignee Email (null) (Assignee Email)
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Resolution Note (string) or Resolution Note (null) (Resolution Note)
Root Cause (string) or Root Cause (null) (Root Cause)
Rule Id (string) or Rule Id (null) (Rule Id)
Severity (string) or Severity (null) (Severity)
Status (string) or Status (null) (Status)
Title (string) or Title (null) (Title)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Put Not Allowed

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Assign Next

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

List Incident Events

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Create Incident Event

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_IncidentEventPayload)
body
required
string (Body)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Export Incident

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Push External Ticket

#155: push this incident to an external ServiceNow / PagerDuty ticket.

Reuses one of the workspace's EXISTING ServiceNow / PagerDuty alert-rule destinations (its stored, encrypted creds) to open one upstream ticket, stamps the external_ticket_* columns on the incident, and appends an external_ticket timeline entry. Push-only: there is no sync-back — a second push is refused (409) so an incident links at most one ticket.

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_ExternalTicketPayload)
alert_rule_id
required
string <uuid> (Alert Rule Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Peek Next Assignee

Authorizations:
HTTPBearer
path Parameters
resource_id
required
string <uuid> (Resource Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

invites

List Invites

List pending invites for this org (unaccepted + unexpired).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Invite

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
email
required
string <email> (Email)
role
string (Role)
Default: "editor"
Enum: "super_admin" "technical_admin" "workspace_admin" "team_admin" "editor" "viewer"
Team Id (string) or Team Id (null) (Team Id)
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "role": "super_admin",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
null

Revoke Invite

Revoke a pending invite.

We delete the row rather than mark it accepted_at — the row's purpose is to track a still-usable invite link, and a revoked invite is no different from one that never existed. The audit row preserves the trail.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
invite_id
required
string <uuid> (Invite Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Org Members

List every user in the org with all their workspace + team memberships.

Issue #649 Part B: a super admin auditing "who exists where" shouldn't have to hop between workspaces. This returns one row per OrgMembership user, each carrying their org role plus the workspace-membership rows and team-membership rows they hold across the whole org — regardless of which workspace is currently selected.

Gating: org-admin tier only (super_admin — see :data:ORG_ADMIN_ROLES). A workspace_admin / technical_admin / team role gets a 403; they keep the workspace-scoped /workspaces/{ws}/members view. The data lives in public-schema tables (org_memberships / workspace_memberships / team_memberships), which the TENANCY-1 workspace-isolation listener does NOT touch, so there's no cross-workspace leak to guard against here beyond the org-id scoping — and the gate keeps non-super-admins from ever reaching this aggregate.

Each member row's workspaces / teams are sorted by name so the UI table is stable without a client-side sort. Returns the standard {"data": [...]} envelope.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

List Workspace Members

List members of a workspace + their workspace role.

Owner / workspace_admin only. Returns the joined User shape so the UI doesn't have to do a second roundtrip per row.

Issue #188: org-level super admins are implicitly present in every workspace of their org — the seed/first-admin flow (and the SSO-JIT provisioning path) creates an OrgMembership row but doesn't always seed a matching WorkspaceMembership. The previous query joined only against workspace_memberships and so reported "No workspace members" for a freshly-seeded org where the owner is the only user. Merge in the org-level super admins so the principal viewing this page always sees themself (and any other super admins) in the list. Workspace- membership rows win the role tie-break when both exist, since that's where the per-workspace UI gestures (role change, remove) take effect.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Add Workspace Member

Give an existing org member access to a workspace at role.

The Members page's per-member "Workspace access" dialog uses this to add someone to a workspace they aren't in yet; PATCH changes the role after that and DELETE removes it. Inviting is still how a new person joins the org. Same authority as PATCH: an org super admin, or a workspace admin of this workspace, capped by the grant ceiling. A super admin already reaches every workspace through their org role, so adding one is a 409.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
role
required
string (Role)
Enum: "workspace_admin" "editor" "viewer"
user_id
required
string <uuid> (User Id)

Responses

Request samples

Content type
application/json
{
  • "role": "workspace_admin",
  • "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
}

Response samples

Content type
application/json
null

Remove Workspace Member

Remove a user from a workspace. The org membership stays put.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Workspace Member

Change a workspace member's role.

Refuses to demote the principal themself — a workspace admin can't accidentally lock themselves out of the admin surface on the same request. (Org owners aren't subject to workspace roles, so the org owner can demote anyone, including their own WorkspaceMembership.)

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
role
required
string (Role)
Enum: "workspace_admin" "editor" "viewer"

Responses

Request samples

Content type
application/json
{
  • "role": "workspace_admin"
}

Response samples

Content type
application/json
null

Set Workspace Member Viewer Access

C-22-4: mark a workspace VIEWER 'restricted' or 'unrestricted'.

A restricted viewer cannot see data assets flagged is_restricted (PII / PHI / PCI) — nor those assets' results, profiles, anomalies or AI suggestions — in this workspace (the do_orm_execute listener in db/workspace_isolation.py enforces it). This is the write path that activates that filter.

Authority: a workspace-admin-or-higher decision, the same bar as flipping an asset's is_restricted flag (CAN_MANAGE_RESTRICTED_FLAG) — the org super admin, or a user whose EFFECTIVE role in this workspace is workspace admin (direct membership or team grant). Only a viewer membership carries a level (400 otherwise). Audited as workspace_membership.viewer_access_updated.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
viewer_access_level
required
string (Viewer Access Level)
Enum: "restricted" "unrestricted"

Responses

Request samples

Content type
application/json
{
  • "viewer_access_level": "restricted"
}

Response samples

Content type
application/json
null

verified-domains

List Join Requests

List pending join requests for this org.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Approve Join Request

Approve a pending join request. Adds the user as an org editor member and stamps the row as decided.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
request_id
required
string <uuid> (Request Id)

Responses

Response samples

Content type
application/json
null

Deny Join Request

Deny a pending join request. Stamps the row decided; no membership is created.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
request_id
required
string <uuid> (Request Id)

Responses

Response samples

Content type
application/json
null

List Verified Domains

List domains claimed by this org (pending + verified).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Claim Verified Domain

Claim a domain. Body {domain}. Returns the verification token and the TXT record the admin must add.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
domain
required
string (Domain)

Responses

Request samples

Content type
application/json
{
  • "domain": "string"
}

Response samples

Content type
application/json
null

Unclaim Verified Domain

Un-claim a domain. Future signups at this address fall back to the default "create new org" flow.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
domain_id
required
string <uuid> (Domain Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Verify Domain

Run a DNS TXT lookup against the claimed domain and flip verified_at if the record matches.

Returns 200 with {verified_at: ...} on success. 422 if no TXT record on the domain carries the expected dq-verify=<token> value, or if the resolver returns NXDOMAIN / no-answer.

DNS work goes through dns.resolver.resolve so tests can monkeypatch the symbol with a stub. The resolver call is wrapped in a tight timeout — a slow / dead DNS server shouldn't hang an admin request indefinitely.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
domain_id
required
string <uuid> (Domain Id)

Responses

Response samples

Content type
application/json
null

personal-api-keys

List Personal Api Keys

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Personal Api Key

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (CreateKeyPayload)
name
required
string (Name)
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Revoke Personal Api Key

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
key_id
required
string <uuid> (Key Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

quotas

Get Org Quotas

Return the org's plan + per-quota usage + limit.

Wire shape::

{
  "data": {
    "plan": "free",
    "upgrade_url": "/settings/billing",
    "quotas": {
      "workspaces":   {"current": 1, "limit": 1},
      "users":        {"current": 3, "limit": 5},
      "datasources":  {"current": 2, "limit": 3},
      "monthly_runs": {"current": 47, "limit": 1000}
    }
  }
}

Any authenticated org member can read this — the matrix isn't sensitive and the UI needs it for the dashboard chrome regardless of role.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{ }

results-all

Org Results All

Super Admin "All workspaces" aggregator (design §3.3).

Org-admin-tier only: callers below super_admin (or the legacy owner / admin compat spellings) get a 403 — they don't have full-org visibility, so an "all workspaces" roll-up would silently lie to them. CAN_VIEW_RESULTS + the default token-type gate keep agent / SCIM tokens out.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
Since (string) or Since (null) (Since)

Inclusive run_time lower bound (ISO 8601). Naive timestamps are interpreted as UTC.

Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string",
  • "summary": {
    }
}

Workspace Results All

Workspace Admin "All teams" aggregator (design §3.3).

Gating: workspace_principal (org match + workspace membership; org admins bypass per HARDEN-17) + CAN_VIEW_RESULTS. Any member tier may call it — the roll-up reflects exactly the slice their pinned workspace + team contexts admit, so a team-tier caller gets their teams' buckets plus workspace_shared, never a sibling team's.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
Since (string) or Since (null) (Since)

Inclusive run_time lower bound (ISO 8601). Naive timestamps are interpreted as UTC.

Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string",
  • "summary": {
    }
}

rule suites (deprecated)

Gone: rule-suites were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: list rule-suites (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Name (string) or Name (null) (Name)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: rule-suites were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Gone: rule-suites were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: get one of rule-suites (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{ }

Gone: rule-suites were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Gone: rule-suites were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Validate Rule Suite Deprecated

DEPRECATED: dry-run lint of a legacy suite's rules (nothing is written).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
suite_id
required
string <uuid> (Suite Id)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: list validation-configs (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Name (string) or Name (null) (Name)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Gone: validation-configs were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: get one of validation-configs (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
resource_id
required
string <uuid> (Resource Id)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Gone: validation-configs were retired Deprecated

path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Gone: rule-suites were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: list rule-suites (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: rule-suites were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Gone: rule-suites were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: get one of rule-suites (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
resource_id
required
string <uuid> (Resource Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Gone: rule-suites were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Gone: rule-suites were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Validate Rule Suite Ws Deprecated

DEPRECATED: dry-run lint of a legacy suite's rules (nothing is written).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
suite_id
required
string <uuid> (Suite Id)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: list validation-configs (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Name (string) or Name (null) (Name)
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Gone: validation-configs were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

DEPRECATED: get one of validation-configs (read-only alias) Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
resource_id
required
string <uuid> (Resource Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Gone: validation-configs were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Gone: validation-configs were retired Deprecated

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

schedules

Get Schedule Timezone

The zone every new schedule runs in, and how existing schedules compare.

Readable by every org member — it names a timezone, nothing secret — so the schedule form and calendar can show times in it. The schedule counts cover only the schedules the caller can see (workspace / team isolation applies).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Update Schedule Timezone

Set the organization schedule timezone — org admins only (audited).

The zone is validated against the IANA database (zoneinfo); an unknown one is a 422. Saving it changes the zone NEW schedules run in and the zone the app shows times in. Existing schedules keep their own zone — and so their firing times — unless align_schedules is true: then every schedule whose zone differs is moved onto this one (its cron is read in the new zone from its next run on) and aligned_schedules says how many.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
align_schedules
boolean (Align Schedules)
Default: false

Also move every existing schedule whose zone differs onto this one, recomputing its next run. Off by default: saving the setting alone never shifts an existing schedule's firing time.

timezone
required
string (Timezone) [ 1 .. 100 ] characters

IANA zone, e.g. 'America/New_York' or 'UTC'.

Responses

Request samples

Content type
application/json
{
  • "align_schedules": false,
  • "timezone": "America/New_York"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Create Schedule Deprecated

DEPRECATED (EPIC PR 8, checkpoints retired): creates a schedule that runs the checkpoint's mapped validation (checkpoints.validation_id). 409 when the checkpoint maps to none. Use POST .../schedules instead.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
checkpoint_id
required
string <uuid> (Checkpoint Id)
Request Body schema: application/json
required
cron_expression
required
string (Cron Expression)
enabled
boolean (Enabled)
Default: true
Team Id (string) or Team Id (null) (Team Id)

Team to scope this schedule to. Omit to inherit the checkpoint's team; send null explicitly to keep the schedule workspace-shared.

Timezone (string) or Timezone (null) (Timezone)

IANA zone the cron is read in. Omit to use the organization schedule timezone (Settings → Scheduling; UTC until an admin sets one).

Responses

Request samples

Content type
application/json
{
  • "cron_expression": "0 6 * * *",
  • "enabled": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "timezone": "string"
}

Response samples

Content type
application/json
null

List Schedules

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Validation Id (string) or Validation Id (null) (Validation Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
null

Create Schedule For Validations

EPIC PR 7: create a schedule that runs one to many validations (validation_ids, required); each firing starts one execution run per validation.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
cron_expression
required
string (Cron Expression)
enabled
boolean (Enabled)
Default: true
Team Id (string) or Team Id (null) (Team Id)

Team to scope this schedule to. Omit to inherit the checkpoint's team; send null explicitly to keep the schedule workspace-shared.

Timezone (string) or Timezone (null) (Timezone)

IANA zone the cron is read in. Omit to use the organization schedule timezone (Settings → Scheduling; UTC until an admin sets one).

Array of Validation Ids (strings) or Validation Ids (null) (Validation Ids)

The validations this schedule runs (1 to 50, same workspace). On .../validations/{id}/schedules they are added to the path's validation; on .../schedules they are required.

Responses

Request samples

Content type
application/json
{
  • "cron_expression": "0 6 * * *",
  • "enabled": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "timezone": "string",
  • "validation_ids": [
    ]
}

Response samples

Content type
application/json
null

Delete Schedule

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
schedule_id
required
string <uuid> (Schedule Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Schedule

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
schedule_id
required
string <uuid> (Schedule Id)
Request Body schema: application/json
required
Cron Expression (string) or Cron Expression (null) (Cron Expression)
Enabled (boolean) or Enabled (null) (Enabled)
Team Id (string) or Team Id (null) (Team Id)
Timezone (string) or Timezone (null) (Timezone)
Array of Validation Ids (strings) or Validation Ids (null) (Validation Ids)

Replace the validations this schedule runs (1 to 50, same workspace).

Responses

Request samples

Content type
application/json
{
  • "cron_expression": "string",
  • "enabled": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "timezone": "string",
  • "validation_ids": [
    ]
}

Response samples

Content type
application/json
null

Create Rule Group Schedule

#712: cron-schedule a validation. EPIC PR 7: validation_ids in the body adds more validations to the same schedule; each firing starts one execution run per validation (worker.jobs.enqueue_scheduled).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
rule_group_id
required
string <uuid> (Rule Group Id)
Request Body schema: application/json
required
cron_expression
required
string (Cron Expression)
enabled
boolean (Enabled)
Default: true
Team Id (string) or Team Id (null) (Team Id)

Team to scope this schedule to. Omit to inherit the checkpoint's team; send null explicitly to keep the schedule workspace-shared.

Timezone (string) or Timezone (null) (Timezone)

IANA zone the cron is read in. Omit to use the organization schedule timezone (Settings → Scheduling; UTC until an admin sets one).

Array of Validation Ids (strings) or Validation Ids (null) (Validation Ids)

The validations this schedule runs (1 to 50, same workspace). On .../validations/{id}/schedules they are added to the path's validation; on .../schedules they are required.

Responses

Request samples

Content type
application/json
{
  • "cron_expression": "0 6 * * *",
  • "enabled": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "timezone": "string",
  • "validation_ids": [
    ]
}

Response samples

Content type
application/json
null

scim-tokens

List Scim Tokens

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Scim Token

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
name
required
string (Name)

Responses

Request samples

Content type
application/json
{
  • "name": "string"
}

Response samples

Content type
application/json
null

Revoke Scim Token

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
token_id
required
string <uuid> (Token Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

service-accounts

List Service Accounts

List the org's live service accounts — name, role, scope, timestamps.

NEVER returns the token or its hash.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Service Account

Mint a service account; returns the raw dqsa_ token exactly once.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
name
required
string (Name) [ 1 .. 255 ] characters
role
required
string (Role)
Enum: "super_admin" "technical_admin" "workspace_admin" "team_admin" "editor" "viewer"
Team Id (string) or Team Id (null) (Team Id)
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "role": "super_admin",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
null

Revoke Service Account

Revoke a service account (soft delete) and bust its auth cache so the dqsa_ token stops working immediately.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
service_account_id
required
string <uuid> (Service Account Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

slack-integration

List Slack Integrations

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
null

Create Slack Integration

Create or ROTATE the org's Slack integration.

Upsert on (org_id, slack_team_id) so re-posting with a new token rotates it in place rather than colliding on the unique constraint.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
bot_token
required
string (Bot Token) non-empty
Slack Team Id (string) or Slack Team Id (null) (Slack Team Id)
Team Name (string) or Team Name (null) (Team Name)

Responses

Request samples

Content type
application/json
{
  • "bot_token": "string",
  • "slack_team_id": "string",
  • "team_name": "string"
}

Response samples

Content type
application/json
null

Delete Slack Integration

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
integration_id
required
string <uuid> (Integration Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Resolve Mentions

Resolve a batch of Slack member ids/mentions to display names.

GRACEFULLY DEGRADES: with no integration configured (or on any Slack error / outage) it returns null for every id plus token_configured: false — NEVER a 500. Subteams / specials / unknown shapes (DECISION-3) resolve to null and pass through at the UI.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
ids
Array of strings (Ids) <= 100 items

Responses

Request samples

Content type
application/json
{
  • "ids": [
    ]
}

Response samples

Content type
application/json
null

Search Slack Users Endpoint

UX gaps D4: search the org's Slack members by NAME for the mention picker.

Backed by Slack users.list (the users:read scope the bot token already needs for users.info) through the connected per-org bot token: the walk follows Slack's pagination cursor, and the reduced {id, name, real_name, handle} directory is cached in Redis per org on a short TTL, so each keystroke searches the cache rather than Slack. The rule still STORES the picked member's id (the delivered message keeps the live <@id> mention).

GRACEFULLY DEGRADES, never a 500: no integration → token_configured: false (the UI keeps its paste-an-id field); a Slack failure → an empty list + a sanitised Slack error CODE. The token is never returned or logged.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
q
string (Q) <= 100 characters
Default: ""

Name to search for (case-insensitive).

limit
integer (Limit) [ 1 .. 50 ]
Default: 20

Max members returned.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": "string",
  • "token_configured": true
}

tags

List Tags

List the org's tag taxonomy. Readable by any org member.

Ordered by name so the asset-editor picker renders a stable, alphabetised list.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "property1": [
    ],
  • "property2": [
    ]
}

Create Tag

Define a new tag. Org-admin only (CAN_MANAGE_ORG).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Colour (string) or Colour (null) (Colour)
Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "colour": "string",
  • "description": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{ }

Delete Tag

Remove a tag from the taxonomy. Org-admin only.

Deleting a definition does NOT strip that string from assets that already carry it (DataAsset.tags is decoupled free-text storage) — the tag just stops being offered in the picker and reads as a legacy/unmanaged tag. This keeps delete cheap and non-destructive to historical classification.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
tag_id
required
string <uuid> (Tag Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Tag

Edit a tag (name / colour / description). Org-admin only.

Only the fields present in the request body are applied — an absent key leaves the stored value unchanged; a key present with null clears an optional field.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
tag_id
required
string <uuid> (Tag Id)
Request Body schema: application/json
required
Colour (string) or Colour (null) (Colour)
Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "colour": "string",
  • "description": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{ }

teams

List Teams

List teams in the org, newest first, each with its member count.

Optionally filter to a single workspace with the workspace_id query param (the workspace's Teams settings page passes it). Org-level listing (no filter) still returns every team — including orphans (workspace_id IS NULL) so an admin can find + re-parent them.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Response samples

Content type
application/json
null

Create Team

Create a new team under a workspace. 409 on a slug collision within that workspace; 404 if the workspace is unknown / in another org.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
External Group Id (string) or External Group Id (null) (External Group Id)
name
required
string (Name) [ 1 .. 80 ] characters
slug
required
string (Slug) [ 2 .. 80 ] characters
workspace_id
required
string <uuid> (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "external_group_id": "string",
  • "name": "string",
  • "slug": "string",
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
null

Delete Team

Delete a team. Cascades wipe its memberships via the FK.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Team

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
null

Update Team

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
External Group Id (string) or External Group Id (null) (External Group Id)
Name (string) or Name (null) (Name)
Slug (string) or Slug (null) (Slug)
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "external_group_id": "string",
  • "name": "string",
  • "slug": "string",
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
null

List Team Members

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
null

Add Team Member

Add a user to a team.

Rejects users that are not :class:OrgMembership rows of the same org — a team is org-scoped by construction. Returns 400 on a cross-org user id (the request is malformed for this org's surface). Existing membership is a no-op idempotent path so SCIM clients can replay the call without surfacing a conflict.

Status code distinguishes the two outcomes so callers (notably SCIM PATCH replays) can tell a fresh insert from a redundant retry:

  • 201 — a new TeamMembership row was inserted.
  • 200 — the user was already on the team; we returned the existing row unchanged.

The decorator deliberately omits status_code so the handler can return a :class:JSONResponse with the right code on each branch (FastAPI's status_code= decorator argument is a single fixed value, which is what produced the original 201-on-no-op bug).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
added_via
string (Added Via)
Default: "manual"
Enum: "manual" "invite"
role
string (Role)
Default: "viewer"
Enum: "team_admin" "editor" "viewer" "workspace_admin"
user_id
required
string <uuid> (User Id)

Responses

Request samples

Content type
application/json
{
  • "added_via": "manual",
  • "role": "team_admin",
  • "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
}

Response samples

Content type
application/json
null

Remove Team Member

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Update Team Member

Change a team member's role and/or a team viewer's access level.

Phase-1 Unit 3: a member's role on the team drives their effective workspace role (the team's workspace_id + this role). A no-op re-role (same value) returns the existing row without an audit event.

C-22-4: viewer_access_level ('restricted' | 'unrestricted') is only valid when the resulting role is viewer (400 otherwise) and takes the workspace-admin bar (:func:ensure_can_set_viewer_access) on the team's workspace — a team admin manages the roster but not data visibility. A role change away from viewer clears the level; a change TO viewer starts at 'unrestricted' unless the body sets one.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
team_id
required
string <uuid> (Team Id)
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
Role (string) or Role (null) (Role)
Viewer Access Level (string) or Viewer Access Level (null) (Viewer Access Level)

Responses

Request samples

Content type
application/json
{
  • "role": "team_admin",
  • "viewer_access_level": "restricted"
}

Response samples

Content type
application/json
null

runs

List Active Runs

Every run in flight in this workspace, newest first. See the module docstring for the three sources, the window, and the scoping.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

ai

List Ai Suggestions

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Kind (string) or Kind (null) (Kind)
Status (string) or Status (null) (Status)
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)

Responses

Response samples

Content type
application/json
null

Request Ai Suggestion

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
required
Kind (string) or Kind (string) (Kind)
Llm Config Id (string) or Llm Config Id (null) (Llm Config Id)
Validation Result Id (string) or Validation Result Id (null) (Validation Result Id)

Responses

Request samples

Content type
application/json
{
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "kind": "sql_rule",
  • "llm_config_id": "6ca9eb9b-0294-4b27-bd8f-f3502be7c77b",
  • "validation_result_id": "75d6a334-625d-4395-bb2a-de2db07abd55"
}

Response samples

Content type
application/json
null

Bulk Accept Suggestions

Accept many AISuggestion rows as rules on their assets in a single request. Per-row errors are reported in the response — a single missing or incompatible suggestion does not block the rest of the batch.

Response shape:

.. code-block:: json

{
  "accepted": ["<uuid>", ...],
  "skipped": [{"id": "<uuid>", "reason": "already accepted"}, ...],
  "failed":  [{"id": "<uuid>", "error": "Suggestion not found"}, ...]
}
Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Rule Suite Id (string) or Rule Suite Id (null) (Rule Suite Id)
suggestion_ids
required
Array of strings <uuid> (Suggestion Ids) [ 1 .. 200 ] items [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "rule_suite_id": "7de675f1-25f4-41f2-b5bd-0134614383d9",
  • "suggestion_ids": [
    ]
}

Response samples

Content type
application/json
{ }

Bulk Dismiss Suggestions

Mark many AISuggestion rows as rejected/dismissed in one transaction.

Uses review_status = "rejected" to stay compatible with the single-row reject path (POST .../review) — the wire-level terminology is "dismiss" (FEATURE-39), but on disk the value is the same one the PARITY-8 reject flow writes. Already-reviewed rows are reported in skipped and left untouched.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Reason (string) or Reason (null) (Reason)
suggestion_ids
required
Array of strings <uuid> (Suggestion Ids) [ 1 .. 200 ] items [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "suggestion_ids": [
    ]
}

Response samples

Content type
application/json
{ }

Get Ai Suggestion

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
suggestion_id
required
string <uuid> (Suggestion Id)

Responses

Response samples

Content type
application/json
null

Accept Suggestion

Add the suggested rules to the target asset and mark accepted.

PARITY-8. The body of the work lives in :func:apply_accept_suggestion so FEATURE-39's bulk-accept path can call exactly the same routine on each suggestion id in its batch — see :mod:dq_cloud_api.routers.ai_bulk.

Authz is per-kind (:func:enforce_accept_gate) and runs AFTER the suggestion is loaded — see the route decorator note.

HARDEN-21 / API review wave-7 (H1): emits the same per-suggestion audit row the bulk path writes, so the timeline carries the same granularity whether the operator clicked "Accept all" or single-stepped through.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
suggestion_id
required
string <uuid> (Suggestion Id)
Request Body schema: application/json
required
NewValidation (object) or null
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Rule Suite Id (string) or Rule Suite Id (null) (Rule Suite Id)
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "create_validation": {
    },
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "rule_suite_id": "7de675f1-25f4-41f2-b5bd-0134614383d9",
  • "validation_ids": [
    ]
}

Response samples

Content type
application/json
null

Review Suggestion

Mark a single AI suggestion as accepted/rejected/dismissed.

HARDEN-21 / API review wave-7 (H2): emits an audit row keyed to the same action vocabulary as ai_bulk.bulk_dismiss_suggestions so the audit timeline has matching granularity whether the user took action via the single-row endpoint or the bulk batch.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
suggestion_id
required
string <uuid> (Suggestion Id)
Request Body schema: application/json
required
Notes (string) or Notes (null) (Notes)
status
required
string (Status)
Enum: "accepted" "rejected"

Responses

Request samples

Content type
application/json
{
  • "notes": "string",
  • "status": "accepted"
}

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Request Profile Summary

Enqueue an LLM job that summarises an asset's latest profile.

Phase-2 native AI. The worker (:func:generate_profile_summary) loads the asset's most recent COMPLETED profile run and writes {summary, findings} to the suggestion's payload. With no profile yet it summarises from the asset's metadata instead and marks the payload profile_based: false + a "run a profile for a better summary" note (asks U4). Read-only — poll GET /ai-suggestions/{id} for the result; there's no accept flow.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
asset_id
required
string <uuid> (Asset Id)
reuse_existing
boolean (Reuse Existing)
Default: false

Responses

Request samples

Content type
application/json
{
  • "asset_id": "b4695157-0d1d-4da0-8f9e-5c53149389e4",
  • "reuse_existing": false
}

Response samples

Content type
application/json
null

Request Severity Review

Ask the AI to propose a severity for each of an asset's rules.

Returns {"data": {"suggestion_id": ...}}. The worker loads the asset's rules (and its latest profile, when there is one), asks the LLM for a relative severity per rule with a one-sentence reason, and writes {reviews, dropped} to the suggestion's payload. Read-only: no rule is changed. 404 for an unknown asset, 422 when the asset has no rules, 400 when no AI engine is configured.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
asset_id
required
string <uuid> (Asset Id)

Responses

Request samples

Content type
application/json
{
  • "asset_id": "b4695157-0d1d-4da0-8f9e-5c53149389e4"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Request Sql Rule Suggestion

Enqueue an LLM job that translates a natural-language rule into a custom-SQL expectation. FEATURE-23.

The worker (:func:generate_sql_rule_suggestion) loads the asset's most recent profile, prompts the configured LLM with the column metadata + the user's English description, validates the returned SQL via :func:dq_shared.expectations.validate_custom_sql_query, and writes {sql, rationale, confidence, natural_language} to the suggestion's payload column. The UI polls GET /ai-suggestions/{id} and renders the proposal.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
asset_id
required
string <uuid> (Asset Id)
natural_language
required
string (Natural Language)

Responses

Request samples

Content type
application/json
{
  • "asset_id": "b4695157-0d1d-4da0-8f9e-5c53149389e4",
  • "natural_language": "string"
}

Response samples

Content type
application/json
null

Request Rule Suggestions

Enqueue an LLM job that authors validated rule proposals for an asset.

Phase-2 native AI. The worker (:func:generate_rule_suggestions) loads the asset's most recent COMPLETED profile + existing rules + the expectation gallery, asks the LLM for {rule_type, kwargs, rationale, confidence} proposals, validates each against the gallery kwargs schema (dropping the invalid ones), and writes {suggestions, dropped} to the payload. Accept the proposals into the asset's suite via POST /ai-suggestions/{id}/accept.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
asset_id
required
string <uuid> (Asset Id)
Guidance (string) or Guidance (null) (Guidance)
reuse_existing
boolean (Reuse Existing)
Default: false

Responses

Request samples

Content type
application/json
{
  • "asset_id": "b4695157-0d1d-4da0-8f9e-5c53149389e4",
  • "guidance": "string",
  • "reuse_existing": false
}

Response samples

Content type
application/json
null

Generate Validation Draft

Start drafting a validation with AI. Returns {suggestion_id, reused}.

Target assets are optional. Named assets must be visible to the caller and in this workspace, and must not mix two restricted teams (422). An identical request by the same user that is still in flight, or finished within the hour and not yet accepted, is reused rather than paid for twice.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Array of Data Asset Ids (strings) or Data Asset Ids (null) (Data Asset Ids)
guidance
required
string (Guidance) [ 3 .. 1000 ] characters

Responses

Request samples

Content type
application/json
{
  • "data_asset_ids": [
    ],
  • "guidance": "string"
}

Response samples

Content type
application/json
{ }

Accept Validation Draft

Create the validation from the reviewed draft, atomically.

Creates the validation (named and described as the user left it), creates the new rules — reusing an existing rule of the asset that checks the same thing — and puts each asset in the validation with exactly the chosen rules. Any failure (a hidden asset, an invalid rule, mixed restricted teams, a rule that isn't the asset's) writes nothing.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
suggestion_id
required
string <uuid> (Suggestion Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
required
Array of objects (Items) [ 1 .. 10 ] items
name
required
string (Name) [ 1 .. 255 ] characters

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "items": [
    ],
  • "name": "string"
}

Response samples

Content type
application/json
{ }

List Anomaly Events

List anomaly events for one workspace, newest-first with cursor pagination.

API review wave-7 / M5: previously this endpoint hard-capped at 200 with no cursor, which was both too small for noisy 24-hour drift windows and incompatible with incremental UI rendering. Now mirrors the GET /audit-events pagination shape — opaque base64 cursor of "<detected_at_iso>|<id>" for stable newest-first iteration through sub-millisecond-collision rows.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
unacknowledged_only
boolean (Unacknowledged Only)
Default: false

Responses

Response samples

Content type
application/json
null

Export Anomaly Events

Download the (filtered) anomaly-events list as an .xlsx workbook (#130 PR 3).

Honours the SAME data_asset_id filter the list endpoint accepts, so a per-asset export (the /anomalies?asset= deep-link) matches the on-screen feed. Gated by CAN_VIEW_RESULTS ("export what you can see") and by scope_to_visible_assets (the same parent-asset visibility gate the list uses — #622 finding 1: anomaly rows leak observed/baseline actuals). The workspace_principal dep + tenant_session keep workspace isolation in force. Reuses the shared export helpers + the EXPORT_MAX_ROWS cap: past it the request 400s and asks the caller to filter by asset. File attachment, NOT the {"data": ...} envelope (the documented download exception).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
format
string (Format) ^(xlsx)$
Default: "xlsx"

Responses

Response samples

Content type
application/json
null

Acknowledge Anomaly

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
event_id
required
string <uuid> (Event Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Account Ai Review

High-level AI account review over the period (Phase 1).

Reuses dashboard_metrics._compute_metrics (health score, pass rate, coverage, severity counts, run counts) as the snapshot, feeds it to account_review_messages, and generates a plain-English executive review live. Graceful: no default LLM config → narrative_status='no_llm_config' (review None), transient provider failure → 'error', both still 200. Returns {"data": AccountReviewResponse}.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
period
integer (Period)
Default: 30

Window in days — one of 30, 60, 90, 365 (the 30/60/90/1y selector).

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Explain Result

Synchronous explain call — suitable for UI tooltips and quick explanations of either a validation result or an anomaly event.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
Anomaly Event Id (string) or Anomaly Event Id (null) (Anomaly Event Id)
Extra Context (string) or Extra Context (null) (Extra Context)
Llm Config Id (string) or Llm Config Id (null) (Llm Config Id)
regenerate
boolean (Regenerate)
Default: false
Validation Result Id (string) or Validation Result Id (null) (Validation Result Id)

Responses

Request samples

Content type
application/json
{
  • "anomaly_event_id": "0a7ae1c5-4599-492f-a9c9-4f69bc3af0a6",
  • "extra_context": "string",
  • "llm_config_id": "6ca9eb9b-0294-4b27-bd8f-f3502be7c77b",
  • "regenerate": false,
  • "validation_result_id": "75d6a334-625d-4395-bb2a-de2db07abd55"
}

Response samples

Content type
application/json
{
  • "cached": false,
  • "completion_tokens": 0,
  • "explanation": "string",
  • "llm_config_id": "6ca9eb9b-0294-4b27-bd8f-f3502be7c77b",
  • "prompt_tokens": 0
}

List Profile Runs

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Status (string) or Status (null) (Status)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Trigger Profile Run

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
data_asset_id
required
string <uuid> (Data Asset Id)
sample_max_rows
integer (Sample Max Rows)
Default: 10000

Responses

Request samples

Content type
application/json
{
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "sample_max_rows": 10000
}

Response samples

Content type
application/json
{
  • "columns": [
    ],
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "error": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "row_count": 0,
  • "run_at": "2019-08-24T14:15:22Z",
  • "sampled_row_count": 0,
  • "schema_hash": "string",
  • "status": "pending"
}

Get Profile Run

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
run_id
required
string <uuid> (Run Id)

Responses

Response samples

Content type
application/json
{
  • "columns": [
    ],
  • "data_asset_id": "65527ff7-0bd6-4962-9e53-09b32d973a2f",
  • "error": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "row_count": 0,
  • "run_at": "2019-08-24T14:15:22Z",
  • "sampled_row_count": 0,
  • "schema_hash": "string",
  • "status": "pending"
}

Cancel Profile Run

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
run_id
required
string <uuid> (Run Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Failures Ai Review

Aggregate AI review of the CURRENT failures over a filtered slice (Phase 1).

Honours the SAME filters as the Results list / summary (start_date / end_date / checkpoint_id / batch_definition_id and — asks U4, additive — rule_group_id, the Results page's "Validation" filter) with status=fail forced, reuses that summary aggregation (failed count, pass rate, per-day trend, worst checkpoints / rule groups) plus a small sample of recent failing rows, and generates a plain-English review. Served from the narrative cache when the identical failures were already reviewed (the worker pre-generates the unfiltered review after a run with failures), so the auto-run card is usually free; refresh=true bypasses the cached copy. Same graceful-degradation contract as the account review. Returns {"data": FailuresReviewResponse}.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Checkpoint Id (string) or Checkpoint Id (null) (Checkpoint Id)
Rule Group Id (string) or Rule Group Id (null) (Rule Group Id)
Start Date (string) or Start Date (null) (Start Date)
End Date (string) or End Date (null) (End Date)
refresh
boolean (Refresh)
Default: false

When true, skip the narrative cache and generate a fresh review (the card's Regenerate). The fresh review then replaces the cached one.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Cached Result Explanation

A validation result's AI explanation IF one is already cached.

The background auto-run (generate_result_explanation) and every POST .../explain warm the cache, so the result page can show the explanation on arrival without spending — even with the explain auto-run switch off, where the panel otherwise waits for a click. explanation is null when nothing is cached yet.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
result_id
required
string <uuid> (Result Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Result Rca

Correlate a failed validation result's already-available signals into a ranked likely-cause summary (FEATURE-410). Returns {"data": RcaResponse}.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
result_id
required
string <uuid> (Result Id)
query Parameters
include_narrative
boolean (Include Narrative)
Default: false

When true and an AI engine is available, append a plain-English AI narrative. Served from the narrative cache when the identical analysis was already narrated (e.g. in the background after the run), so repeat page opens don't re-spend. Off by default.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

asset-dependencies

Delete Dependency

Owner / admin only.

Workspace membership is already enforced by workspace_principal; the extra org-role check restricts mutations to people who can edit catalog metadata (owners + admins). Editors can view the graph but can't tear edges down.

Authorizations:
HTTPBearer
path Parameters
dep_id
required
string <uuid> (Dep Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

List Dependencies

List direct upstream + downstream edges for one asset.

Returns two lists rather than one combined edge list so the UI can render "Upstream of X" / "Downstream of X" sections without re-classifying each row client-side.

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "property1": [
    ],
  • "property2": [
    ]
}

Create Dependency

Create one upstream edge for asset_id.

The path asset is the downstream end (the consuming asset). The body supplies upstream — either another asset in the same org or a free-form external pointer.

Cross-org dependencies are rejected: when upstream_asset_id is supplied we verify it resolves inside the current tenant schema. The RLS policy alone wouldn't catch this — a row with an FK to an asset that doesn't exist in this tenant's data_assets would fail the FK at write time, but the error message would be a confusing SQL state. We pre-check so the 400 is explicit and uniform.

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_DependencyPayload)
source
string (Source) <= 20 characters
Default: "manual"
Upstream Asset Id (string) or Upstream Asset Id (null) (Upstream Asset Id)
_UpstreamExternal (object) or null

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Get Lineage

Walk the lineage graph up to depth hops in both directions.

Returns {"data": {"nodes": [...], "edges": [...]}} where nodes carry a kind of either "asset" (resolved via a DataAsset row, with name/type for label rendering) or "external" (carrying the system + model + url from upstream_external).

Walk is cycle-safe: a walked set of asset IDs short-circuits revisits, and we cap the BFS depth even before that to bound payload size. Both an A→B→A cycle and a fan-out star are handled in O(N + E) within the cap.

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
depth
integer (Depth)
Default: 3

Responses

Response samples

Content type
application/json
{ }

batch-definitions

Delete Batch Definition

Owner / admin only — same authority gradient as the rest of the catalog mutations (asset_dependencies, alert rules, etc.). Editors can list and read but can't tear a batch definition down.

validation_configs.batch_definition_id has ON DELETE SET NULL so deleting a definition leaves the linked configs pointing at NULL (the worker interprets that as "whole asset"), avoiding an orphaned FK error. The UI surfaces a soft warning when a definition still has configs pointed at it.

Authorizations:
HTTPBearer
path Parameters
bd_id
required
string <uuid> (Bd Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Batch Definition

Authorizations:
HTTPBearer
path Parameters
bd_id
required
string <uuid> (Bd Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

List Batch Definitions

List every batch definition attached to one asset.

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Create Batch Definition

Create one batch definition for asset_id.

The body is validated through the shared :func:validate_batch_definition helper — the worker uses the same function so a row accepted here is guaranteed to be renderable. Unknown config keys are dropped (the validator returns a freshly constructed config dict).

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_BatchDefinitionPayload)
object (Config)
kind
required
string (Kind) [ 1 .. 32 ] characters
name
string (Name) <= 255 characters
Default: ""

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

dbt

Ingest Dbt Artifacts

Synchronously parse a (manifest, run_results) pair and write the resulting :class:DataAsset / :class:ValidationResult rows.

Returns {"data": {assets_registered, assets_updated, results_imported, rule_suite_id, unmatched_run_results}} — the UI shows the first three on a flash message after the upload.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
datasource_id
required
string <uuid> (Datasource Id)
required
object (Manifest)
Run Results (object) or Run Results (null) (Run Results)

Responses

Request samples

Content type
application/json
{
  • "datasource_id": "11f86609-0961-4dd0-a490-1432bc861567",
  • "manifest": { },
  • "run_results": { }
}

Response samples

Content type
application/json
{ }

run-queue

Get Run Queue Row

Return the RunQueue row, scoped to the caller's org + workspace.

See module docstring for the authorisation rules and the cross-tenant 404 posture.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
run_queue_id
required
string <uuid> (Run Queue Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel Run Queue Row

Request a cooperative cancel of one run-queue row.

Sets cancel_requested = True when the row is still pending / running. The worker checks it before the run starts and polls it while the runner works, so a direct run already in progress is interrupted within a few seconds and ends cancelled with no result (an agent-mode run the on-prem agent already started finishes there). Idempotent: a terminal row (completed / failed / cancelled) is a no-op 200 echoing its current status rather than a 409 — "there is nothing to stop" is not an error, and a UI polling the run shouldn't have to special-case the race where the run finished a beat before Stop was clicked.

If the row is a rule-group parent's MEMBER row, this cancels just that member (the group-level cancel that flags every member is POST .../validations/{id}/run/cancel).

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
run_queue_id
required
string <uuid> (Run Queue Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

search

Global Search

Return grouped, workspace-scoped search hits across first-class objects.

Blank / whitespace q yields an empty list rather than a full-table dump. limit is capped per object type so a broad term can't return an unbounded result set.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
q
required
string (Q)

Search term; matched as a case-insensitive substring.

Type (string) or Type (null) (Type)

Optional scope filter — restrict to one hit type (datasource / data_asset / rule_group / schedule / alert / result / column). Incidents are not searched while they are hidden (#144).

limit
integer (Limit) [ 1 .. 25 ]
Default: 8

Max hits returned per object type.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

result-comments

List Comments

Authorizations:
HTTPBearer
path Parameters
result_id
required
string <uuid> (Result Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "property1": [
    ],
  • "property2": [
    ]
}

Create Comment

Create a new comment on a validation result.

API review wave-7 (M2): Pydantic body replaces the previous await request.json() hand-parse. The model enforces the FEATURE-22 1..8192-codepoint cap up front; the body still goes through _validate_body to honour the UTF-8-byte cap for multibyte text and to trim trailing whitespace consistently with the previous behaviour.

Authorizations:
HTTPBearer
path Parameters
result_id
required
string <uuid> (Result Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_ResultCommentPayload)
body
required
string (Body)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Delete Comment

Authorizations:
HTTPBearer
path Parameters
result_id
required
string <uuid> (Result Id)
comment_id
required
string <uuid> (Comment Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Edit Comment

Edit a comment. Author + org-owner only (see :func:_can_modify).

API review wave-7 (M2): typed body — same shape as create.

Authorizations:
HTTPBearer
path Parameters
result_id
required
string <uuid> (Result Id)
comment_id
required
string <uuid> (Comment Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (_ResultCommentPayload)
body
required
string (Body)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

health

Get Asset Health

Window health for one data asset (404 when it isn't visible).

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, the health score covers. Default 30; 1-365.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Asset Health

Window health for every data asset with a run in the window.

Authorizations:
HTTPBearer
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, the health score covers. Default 30; 1-365.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get Asset Health

Window health for one data asset (404 when it isn't visible).

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, the health score covers. Default 30; 1-365.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Asset Health

Window health for every data asset with a run in the window.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, the health score covers. Default 30; 1-365.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

validation-rules

List Asset First Class Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)

Responses

Response samples

Content type
application/json
{ }

Asset Rule Validation Memberships

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)

Responses

Response samples

Content type
application/json
{ }

Put Rule Validations

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
Request Body schema: application/json
required
required
object (RuleValidationsWrite)

Body for PUT .../data-assets/{asset_id}/rules/{rule_id}/validations — the curated validations the rule should be in (set semantics). The asset's default always keeps it and isn't listed; a validation that includes all of the asset's rules can't drop one (409).

NewValidation (object) or null
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Assets

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Remove Validation Asset

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
asset_id
required
string <uuid> (Asset Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Set Validation Asset Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
asset_id
required
string <uuid> (Asset Id)
Request Body schema: application/json
required
required
object (ValidationAssetInclusion)

Body for PUT .../validations/{id}/assets/{asset_id}/rules — set which of an asset's rules the validation holds (set semantics; idempotent).

mode="all" holds every rule of the asset, and rules added to it later join automatically. mode="selected" holds exactly rule_ids (at least one) and nothing joins later. batch_definition_id, when sent, scopes the asset's rules in this validation to a partition (null = the whole asset); omitted, each rule keeps its current scope.

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
mode
required
string (Mode)
Enum: "all" "selected"
Array of Rule Ids (strings) or Rule Ids (null) (Rule Ids)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{ }

Add Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
required
object (ValidationRuleAdd)

Body for POST .../validations/{id}/rules — add one rule to a validation.

batch_definition_id optionally scopes this rule's run to a partition / interval within its asset (§11.4).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
rule_id
required
string <uuid> (Rule Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Remove Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
rule_id
required
string <uuid> (Rule Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Patch Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
rule_id
required
string <uuid> (Rule Id)
Request Body schema: application/json
required
required
object (ValidationRuleBatchPatch)

Body for PATCH .../validations/{id}/rules/{rule_id} — scope one rule's run in this validation to a partition (null = the whole asset).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Any of
string <uuid> (Batch Definition Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Run Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List Asset First Class Rules

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Asset Rule Validation Memberships

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Put Rule Validations

Authorizations:
HTTPBearer
path Parameters
asset_id
required
string <uuid> (Asset Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleValidationsWrite)

Body for PUT .../data-assets/{asset_id}/rules/{rule_id}/validations — the curated validations the rule should be in (set semantics). The asset's default always keeps it and isn't listed; a validation that includes all of the asset's rules can't drop one (409).

NewValidation (object) or null
validation_ids
Array of strings <uuid> (Validation Ids) [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Assets

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Remove Validation Asset

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Set Validation Asset Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
asset_id
required
string <uuid> (Asset Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (ValidationAssetInclusion)

Body for PUT .../validations/{id}/assets/{asset_id}/rules — set which of an asset's rules the validation holds (set semantics; idempotent).

mode="all" holds every rule of the asset, and rules added to it later join automatically. mode="selected" holds exactly rule_ids (at least one) and nothing joins later. batch_definition_id, when sent, scopes the asset's rules in this validation to a partition (null = the whole asset); omitted, each rule keeps its current scope.

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
mode
required
string (Mode)
Enum: "all" "selected"
Array of Rule Ids (strings) or Rule Ids (null) (Rule Ids)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Add Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (ValidationRuleAdd)

Body for POST .../validations/{id}/rules — add one rule to a validation.

batch_definition_id optionally scopes this rule's run to a partition / interval within its asset (§11.4).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
rule_id
required
string <uuid> (Rule Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Remove Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Patch Validation Rule

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
rule_id
required
string <uuid> (Rule Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (ValidationRuleBatchPatch)

Body for PATCH .../validations/{id}/rules/{rule_id} — scope one rule's run in this validation to a partition (null = the whole asset).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Any of
string <uuid> (Batch Definition Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Validation Run Rules

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

execution-runs

Get Execution Run

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Execution Run Progress

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Execution Run Results

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)

Responses

Response samples

Content type
application/json
{ }

List Execution Runs

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Get Execution Run

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get Execution Run Progress

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Execution Run Results

Authorizations:
HTTPBearer
path Parameters
run_id
required
string <uuid> (Run Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

List Execution Runs

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

rule-templates

List Rule Templates

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Create Rule Template

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (RuleTemplateCreate)

Body for POST .../rule-templates (inside the data envelope).

workspace_id / team_id are not accepted here — the route clamps the workspace from the URL (org-scoped create leaves it NULL = workspace-shared) and team scoping is a later unit. rules defaults to empty so a template can be created as a shell and filled in later.

Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
Array of objects (Rules)
tags
Array of strings (Tags)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Delete Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)

Responses

Response samples

Content type
application/json
{ }

Patch Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)
Request Body schema: application/json
required
required
object (RuleTemplateUpdate)

Body for PATCH .../rule-templates/{id} (inside the data envelope).

Every field is optional — an omitted field leaves the column unchanged. name must be non-empty when supplied. rules / tags accept an explicit null which the route coerces to [] (both are NOT NULL columns, so a raw None would raise on commit); omitting them leaves the column unchanged. A supplied rules REPLACES the template's rule list (a template edit is a whole-list save, like the suite editor).

Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Array of Rules (objects) or Rules (null) (Rules)
Array of Tags (strings) or Tags (null) (Tags)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

List Rule Templates

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Create Rule Template

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleTemplateCreate)

Body for POST .../rule-templates (inside the data envelope).

workspace_id / team_id are not accepted here — the route clamps the workspace from the URL (org-scoped create leaves it NULL = workspace-shared) and team scoping is a later unit. rules defaults to empty so a template can be created as a shell and filled in later.

Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
Array of objects (Rules)
tags
Array of strings (Tags)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Delete Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{ }

Patch Rule Template

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleTemplateUpdate)

Body for PATCH .../rule-templates/{id} (inside the data envelope).

Every field is optional — an omitted field leaves the column unchanged. name must be non-empty when supplied. rules / tags accept an explicit null which the route coerces to [] (both are NOT NULL columns, so a raw None would raise on commit); omitting them leaves the column unchanged. A supplied rules REPLACES the template's rule list (a template edit is a whole-list save, like the suite editor).

Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Array of Rules (objects) or Rules (null) (Rules)
Array of Tags (strings) or Tags (null) (Tags)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

rules

List Rules

The live rules the caller can see (optionally one asset's / one validation's), newest first.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
query Parameters
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Validation Id (string) or Validation Id (null) (Validation Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Get Rule

One rule, with the (visible) validations that hold it.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
rule_id
required
string <uuid> (Rule Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Rules

The live rules the caller can see (optionally one asset's / one validation's), newest first.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
Validation Id (string) or Validation Id (null) (Validation Id)
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)

Responses

Response samples

Content type
application/json
{ }

Get Rule

One rule, with the (visible) validations that hold it.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
rule_id
required
string <uuid> (Rule Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

validations

List Rule Groups

Authorizations:
HTTPBearer
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, for window_health (the headline health score). Default 30; 1-365.

include_defaults
boolean (Include Defaults)
Default: true

Include assets' default validations (is_default). Pass false for curated validations only.

Responses

Response samples

Content type
application/json
{ }

Create Rule Group

Authorizations:
HTTPBearer
Request Body schema: application/json
required
required
object (RuleGroupCreate)

Body for POST .../validations (inside the data envelope).

health_score / health_computed_at are deliberately absent: they are server-computed at run time (PR 3) and can never be set by a client. workspace_id is not accepted — the route clamps it from the URL.

team_id is the optional owning team (EPIC PR 5; null = workspace- shared). It is a label unless team_restricted is true, which limits visibility to that team — a workspace-admin decision.

Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
tags
Array of strings (Tags)
Team Id (string) or Team Id (null) (Team Id)
team_restricted
boolean (Team Restricted)
Default: false

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

List Asset Rule Group Memberships

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Delete Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, for window_health (the headline health score). Default 30; 1-365.

Responses

Response samples

Content type
application/json
{ }

Patch Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
required
object (RuleGroupUpdate)

Body for PATCH .../validations/{id} (inside the data envelope).

Every field is optional — an omitted field leaves the column unchanged. name must be non-empty when supplied. The health columns are read-only and cannot be patched. tags accepts an explicit null which the route coerces to [] (clear to empty) — the column is NOT NULL, so a raw None would otherwise raise on commit; omitting tags leaves it unchanged.

team_id (EPIC PR 5): an explicit null makes the validation workspace-shared; omit it to leave the team unchanged. Assigning a team is editor tier; changing or clearing one, and flipping team_restricted, need workspace-admin authority on the validation's workspace. An asset's default validation takes its asset's team and rejects both fields.

Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Array of Tags (strings) or Tags (null) (Tags)
Team Id (string) or Team Id (null) (Team Id)
Team Restricted (boolean) or Team Restricted (null) (Team Restricted)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

List Rule Group Members Deprecated

DEPRECATED: the assets this validation's rules cover (derived).

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Gone: the asset list was retired (add rules instead) Deprecated

path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
Any of
required
object (RuleGroupMemberCreate)

Body for POST .../validations/{id}/members (inside the data envelope).

RETIRED (EPIC PR 9): the asset-list write returns 410 Gone. Add rules to the validation instead (POST .../validations/{id}/rules or the asset's per-asset picker).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
rule_suite_id
required
string <uuid> (Rule Suite Id)

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Gone: the asset list was retired (remove rules instead) Deprecated

path Parameters
group_id
required
string <uuid> (Group Id)
member_id
required
string <uuid> (Member Id)

Responses

Response samples

Content type
application/json
null

Run Rule Group

Run the validation: exactly its live, enabled rules (EPIC PR 3).

The rules run one GX pass per (asset, effective batch) unit — each rule on its membership batch binding unless the optional body overrides it (batch_definition_id + partition_value / partition_values, several values = several runs), or restricts the run to some of the validation's rules (rule_ids). See ValidationRunRequest.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
Any of
object (ValidationRunRequest)

Optional body of POST .../validations/{id}/run (EPIC PR 3).

A run executes the validation's live, enabled rules, one GX pass per (asset, effective batch). Each rule's batch is its membership binding unless this run overrides it:

  • batch_definition_id — run the rules on this definition's asset against it (every other asset keeps its own bindings). The asset must hold a rule in the validation and run direct (the DQ Agent validates an asset whole);
  • partition_value / partition_values — the partition(s) of a column_partition definition to validate. Several values start one run each (FEATURE-37 parity), capped at :data:VALIDATION_RUN_MAX_PARTITIONS;
  • rule_ids — run only these rules of the validation.

Omit the body (or send {}) to run every rule on its own binding.

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Partition Value (string) or Partition Value (integer) or Partition Value (null) (Partition Value)
(Array of Partition Values (strings or integers)) or Partition Values (null) (Partition Values)
Array of Rule Ids (strings) or Rule Ids (null) (Rule Ids)

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel Rule Group Run

Cooperatively cancel one in-flight run of this validation.

Flags cancel_requested = True on every NON-TERMINAL run_queue row of the run (the agent-mode members still out at the DQ Agent, keyed on rule_group_id + parent_result_id) AND records the intent on the parent rollup so the worker's in-process DIRECT-member loop sees it. A direct asset already running is interrupted (the worker polls the flag while its runner works and terminates it within a few seconds, writing no result for it); pending assets never start and flip to cancelled.

Best-effort for agent members: the flag is honoured only if the on-prem agent hasn't already claimed + started the member (it runs autonomously and we do NOT hard-kill it). Idempotent: re-issuing on the same run re-flags the same rows (already-flagged / already-terminal rows are unaffected) and returns the current live count.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
required
object (RuleGroupRunCancelInput)

The cancel request payload (run-engine Phase 1a).

parent_result_id identifies WHICH run to cancel — the parent rollup ValidationResult the run endpoint returned. A validation accrues many runs in its history, so the cancel targets one specific in-flight run rather than guessing the latest.

Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
parent_result_id
required
string <uuid> (Parent Result Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

List Rule Groups

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
Limit (integer) or Limit (null) (Limit)
Cursor (string) or Cursor (null) (Cursor)
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, for window_health (the headline health score). Default 30; 1-365.

include_defaults
boolean (Include Defaults)
Default: true

Include assets' default validations (is_default). Pass false for curated validations only.

Responses

Response samples

Content type
application/json
{ }

Create Rule Group

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleGroupCreate)

Body for POST .../validations (inside the data envelope).

health_score / health_computed_at are deliberately absent: they are server-computed at run time (PR 3) and can never be set by a client. workspace_id is not accepted — the route clamps it from the URL.

team_id is the optional owning team (EPIC PR 5; null = workspace- shared). It is a label unless team_restricted is true, which limits visibility to that team — a workspace-admin decision.

Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
tags
Array of strings (Tags)
Team Id (string) or Team Id (null) (Team Id)
team_restricted
boolean (Team Restricted)
Default: false

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

List Asset Rule Group Memberships

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Delete Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
window_days
integer (Window Days) [ 1 .. 365 ]
Default: 30

Trailing window, in days, for window_health (the headline health score). Default 30; 1-365.

Responses

Response samples

Content type
application/json
{ }

Patch Rule Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleGroupUpdate)

Body for PATCH .../validations/{id} (inside the data envelope).

Every field is optional — an omitted field leaves the column unchanged. name must be non-empty when supplied. The health columns are read-only and cannot be patched. tags accepts an explicit null which the route coerces to [] (clear to empty) — the column is NOT NULL, so a raw None would otherwise raise on commit; omitting tags leaves it unchanged.

team_id (EPIC PR 5): an explicit null makes the validation workspace-shared; omit it to leave the team unchanged. Assigning a team is editor tier; changing or clearing one, and flipping team_restricted, need workspace-admin authority on the validation's workspace. An asset's default validation takes its asset's team and rejects both fields.

Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Array of Tags (strings) or Tags (null) (Tags)
Team Id (string) or Team Id (null) (Team Id)
Team Restricted (boolean) or Team Restricted (null) (Team Restricted)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

List Rule Group Members Deprecated

DEPRECATED: the assets this validation's rules cover (derived).

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Gone: the asset list was retired (add rules instead) Deprecated

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
Any of
required
object (RuleGroupMemberCreate)

Body for POST .../validations/{id}/members (inside the data envelope).

RETIRED (EPIC PR 9): the asset-list write returns 410 Gone. Add rules to the validation instead (POST .../validations/{id}/rules or the asset's per-asset picker).

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
rule_suite_id
required
string <uuid> (Rule Suite Id)

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
null

Gone: the asset list was retired (remove rules instead) Deprecated

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
member_id
required
string <uuid> (Member Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)

Responses

Response samples

Content type
application/json
null

Run Rule Group

Run the validation: exactly its live, enabled rules (EPIC PR 3).

The rules run one GX pass per (asset, effective batch) unit — each rule on its membership batch binding unless the optional body overrides it (batch_definition_id + partition_value / partition_values, several values = several runs), or restricts the run to some of the validation's rules (rule_ids). See ValidationRunRequest.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
Any of
object (ValidationRunRequest)

Optional body of POST .../validations/{id}/run (EPIC PR 3).

A run executes the validation's live, enabled rules, one GX pass per (asset, effective batch). Each rule's batch is its membership binding unless this run overrides it:

  • batch_definition_id — run the rules on this definition's asset against it (every other asset keeps its own bindings). The asset must hold a rule in the validation and run direct (the DQ Agent validates an asset whole);
  • partition_value / partition_values — the partition(s) of a column_partition definition to validate. Several values start one run each (FEATURE-37 parity), capped at :data:VALIDATION_RUN_MAX_PARTITIONS;
  • rule_ids — run only these rules of the validation.

Omit the body (or send {}) to run every rule on its own binding.

Batch Definition Id (string) or Batch Definition Id (null) (Batch Definition Id)
Partition Value (string) or Partition Value (integer) or Partition Value (null) (Partition Value)
(Array of Partition Values (strings or integers)) or Partition Values (null) (Partition Values)
Array of Rule Ids (strings) or Rule Ids (null) (Rule Ids)

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel Rule Group Run

Cooperatively cancel one in-flight run of this validation.

Flags cancel_requested = True on every NON-TERMINAL run_queue row of the run (the agent-mode members still out at the DQ Agent, keyed on rule_group_id + parent_result_id) AND records the intent on the parent rollup so the worker's in-process DIRECT-member loop sees it. A direct asset already running is interrupted (the worker polls the flag while its runner works and terminates it within a few seconds, writing no result for it); pending assets never start and flip to cancelled.

Best-effort for agent members: the flag is honoured only if the on-prem agent hasn't already claimed + started the member (it runs autonomously and we do NOT hard-kill it). Idempotent: re-issuing on the same run re-flags the same rows (already-flagged / already-terminal rows are unaffected) and returns the current live count.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
Request Body schema: application/json
required
required
object (RuleGroupRunCancelInput)

The cancel request payload (run-engine Phase 1a).

parent_result_id identifies WHICH run to cancel — the parent rollup ValidationResult the run endpoint returned. A validation accrues many runs in its history, so the cancel targets one specific in-flight run rather than guessing the latest.

Data Asset Id (string) or Data Asset Id (null) (Data Asset Id)
parent_result_id
required
string <uuid> (Parent Result Id)

Responses

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

datasources

Discover Assets

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
datasource_id
required
string <uuid> (Datasource Id)

Responses

Response samples

Content type
application/json
null

Register Discovered Assets

Atomically register the supplied discovered tables as DataAssets.

Replaces the modal's N sequential client-side POST /data-assets calls with one transaction so a half-success can't leave a partial state. Each table is find-or-created keyed by (datasource_id, name) — re-registering an existing name is a no-op (counted as skipped), so the call is idempotent.

Returns {"data": {assets_registered, assets_skipped, asset_ids}}; asset_ids carries every resolved asset id (created or pre-existing) so the UI can navigate straight to them.

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
datasource_id
required
string <uuid> (Datasource Id)
Request Body schema: application/json
required
Array of objects (Tables)
Array
Format (string) or Format (null) (Format)
kind
string (Kind)
Default: "table"
name
required
string (Name)
Path (string) or Path (null) (Path)
Row Count Estimate (integer) or Row Count Estimate (null) (Row Count Estimate)
schema
required
string (Schema)

Responses

Request samples

Content type
application/json
{
  • "tables": [
    ]
}

Response samples

Content type
application/json
{ }

Test Datasource Connection

Authorizations:
HTTPBearer
path Parameters
org_id
required
string <uuid> (Org Id)
workspace_id
required
string <uuid> (Workspace Id)
datasource_id
required
string <uuid> (Datasource Id)

Responses

Response samples

Content type
application/json
null

auth

Accept Invite

Consume a pending team invite and start a session.

Public, unauthenticated endpoint — anyone holding the raw dqi_ token can call it (that's the point of an invite link). The handler:

  1. Looks the invite up by token_hash.
  2. Rejects rows that are missing, already accepted, or past their expires_at. All three failure modes return the same opaque 400 so a probing client can't distinguish "already used" from "expired" from "never existed".
  3. Reuses the existing User if the invite email already maps to one; otherwise creates a fresh row (requires password + the optional full_name).
  4. Idempotently upserts the OrgMembership (and the WorkspaceMembership when the invite is workspace-scoped) at the invite's role.
  5. Stamps accepted_at so the row can't be replayed.
  6. Issues an access token + refresh cookie identical to /auth/login so the UI can call the user authenticated to the new org / workspace.

Rate limiting is not applied here — the invite token itself is the single-use credential, and an attacker who can guess a 32-char base62 token has bigger problems than we can solve in this layer.

Request Body schema: application/json
required
Full Name (string) or Full Name (null) (Full Name)
Password (string) or Password (null) (Password)
token
required
string (Token)

Responses

Request samples

Content type
application/json
{
  • "full_name": "string",
  • "password": "string",
  • "token": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "csrf_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string",
  • "token_type": "bearer"
}

Elevate

Re-prove identity with the user's password and return a fresh access token (auth_fresh_at = now).

The refresh cookie is intentionally NOT rotated: only the access-token claims update. CSRF protection isn't applied because this is an Authorization-bearer flow (no cookie carries the credential being challenged); rate limiting matches /auth/login so it's as hard to brute-force.

Personal API keys and agent tokens (dqk_ / dqa_) cannot use this endpoint — they have no associated user password. We surface that as 401 invalid credentials for the agent-token path (no user to look up) without branching on token type, since the DB lookup naturally fails when principal.user_id is None.

Authorizations:
HTTPBearer
Request Body schema: application/json
required
password
required
string (Password)

Responses

Request samples

Content type
application/json
{
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "csrf_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string",
  • "token_type": "bearer"
}

Forgot Password

#43: begin a self-service password reset.

Enumeration-safe: every path returns the same 200 ack. A reset link is only minted + emailed when the email maps to an ACTIVE account that actually has a local password (SSO-only users have hashed_password IS NULL and are silently skipped — there's nothing to reset), and only when the deployment is configured to deliver mail (PUBLIC_BASE_URL for the link + SMTP_HOST for delivery). Requesting a new link invalidates the account's previous unused links so only the newest one works.

Request Body schema: application/json
required
email
required
string <email> (Email)

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com"
}

Response samples

Content type
application/json
{
  • "message": "string"
}

Iap Exchange

Exchange a Google IAP assertion for a DQ Cloud session.

Trusts the X-Goog-IAP-JWT-Assertion header that Cloud Load Balancing's IAP forwards to backends when available. The endpoint validates the JWT signature against Google's JWKS, checks audience

  • issuer, and JIT-provisions a User keyed by the email claim.

Deployment shape: the UI sits behind an external HTTPS LB with IAP enabled. On page load the UI POSTs to this endpoint; if it returns a session, the password-login form is skipped.

Two modes, picked by configuration with NO fall-through (#280):

  • Verified mode (settings.iap_audience set): require the signed X-Goog-IAP-JWT-Assertion header and verify it against Google's JWKS for the configured audience. A missing or invalid assertion is a hard 401 — the header-trust branch is disabled in this mode so a stripped-JWT request cannot downgrade verified mode into header-trust mode.

  • Header-trust fallback (settings.iap_audience empty): trust the X-Goog-Authenticated-User-Email / X-Goog-Authenticated-User-Id header pair. Cloud Run-backed serverless NEGs behind IAP only consistently forward those headers — the signed JWT assertion is dropped somewhere on the Cloud Run path even with IAP enabled at the LB. Header trust is safe in that configuration because:

    1. The API is ingress = INGRESS_TRAFFIC_INTERNAL_ONLY, so the only callers are other resources in the project's VPC.
    2. The only VPC resource that proxies to the API is the UI's nginx, which forwards headers verbatim from IAP-issued requests off the load balancer.
    3. End-user requests must pass IAP at the LB layer before they can reach the UI at all, and IAP is the source of these headers.

    The header carries an accounts.google.com: prefix (IAP's subject-namespace convention) — strip it.

JIT-provisioning is gated by :attr:Settings.iap_jit_provision_domains: only emails whose domain is on the allow-list get auto-created. New users join :attr:Settings.iap_default_org_slug at the role named by :attr:Settings.iap_default_jit_role (default :attr:Role.EDITOR; see _iap_default_jit_role) with no password — the IAP layer is the only auth surface for these accounts. PR #275 normalised every legacy "member" role to "editor", so we mint the canonical enum value here rather than the old string.

Existing-account hijack defence (#281): when the IAP-asserted email maps to an existing user, _assert_iap_signin_allowed runs before we mint a session. It refuses with 403 when the account has a password (and isn't IAP-linked), when it carries a non-IAP external_identity, or when the user's org has an enforced SSO IdentityProvider that isn't IAP.

Runs BEFORE workspace context is set on the session, so it operates with the unrestricted-bypass GUC state (TENANCY-2 #273). The endpoint only touches public-schema tables (users, org_memberships, workspaces, workspace_memberships, refresh_tokens) so the missing tenant search_path is fine.

Responses

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "csrf_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string",
  • "token_type": "bearer"
}

Login

Request Body schema: application/json
required
email
required
string <email> (Email)
password
required
string (Password)

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "csrf_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string",
  • "token_type": "bearer"
}

Logout

Responses

Me Permissions

RBAC-3: return the calling principal's role + flattened permission set.

The UI mirrors the server's RBAC-1 matrix here rather than hard-coding a TypeScript copy: the source of truth lives in :mod:dq_cloud_api.auth.roles, and GET /auth/me/permissions projects it through the caller's role so the SPA can hide / disable controls the backend would 403 anyway.

Response shape (stable; the UI's usePermissions() hook depends on it)::

{
  "role": "editor",
  "permissions": ["can_view_results", "can_create_datasource", ...]
}

Notes:

  • Permissions are emitted as their canonical string values (the Permission enum's value), not the Python member names. That lines up with everything else on the wire — JWT claims, DB columns, audit metadata — and lets the UI compare against the same literals.
  • Programmatic tokens (Role.AGENT / Role.SCIM) and any unknown role string return an empty permission list. They authorise through a different gate (workspace pinning / SCIM route gate) and shouldn't drive UI affordances on the human-RBAC matrix.
  • principal.role is org-scoped, so it's resolved with :func:coerce_org_role — same behaviour as :func:has_org_permission, which is what require_permission uses. A stored JWT from an older release still resolves cleanly: legacy "member" → editor and (the org-scope difference) legacy "admin" → super_admin, so the projected permission set the UI gates against matches the set the API will actually enforce.
  • No DB hit: everything we need is on principal. That keeps the endpoint cheap enough for the UI to call on every navigation if it ever wanted to (the TanStack Query layer caches 5 min today).
  • No fresh_auth / step-up gate. The endpoint is read-only and surfaces no secret beyond what the JWT itself already carries.
Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
{ }

Me Principal

#366: return the calling user's identity for the app nav/header.

The JWT carries only opaque ids (sub / org / ws) plus the role — enough to authorise, not enough to display who is logged in. The nav has no human-readable identity, so a user juggling multiple accounts can't tell which one is active without logging out. This endpoint resolves the principal's sub to the stored email / full_name and pairs it with the org name + role so the sidebar can render an identity block.

Response shape (the UI's useMe() hook depends on it)::

{
  "user_id": "…",
  "email": "ada@example.com",
  "full_name": "Ada Lovelace",
  "role": "editor",
  "org_id": "…",
  "org_name": "Acme",
  "workspace_id": "…" | null,
  "ai_available": true,
  "team_ids": ["…"]
}

Notes:

  • Programmatic tokens (dqa_ / dqs_) have no associated user (principal.user_id is None) and no human identity to surface; they get a 404 — the nav only renders for browser sessions, which always carry a user JWT.
  • ai_available (Phase-1 hybrid) is the org-level, viewer-readable "is any AI engine reachable?" flag — native in effect OR a default BYOLLM config present, computed by :func:ai_available so it can't drift from the resolver. It lets a non-LLM-manager (editor / viewer) know AI works in native mode without reading the CAN_MANAGE_LLM_CONFIGS-gated ai-policy endpoint, fixing the dead "configure a provider" button.
  • One indexed primary-key read per call. The UI caches it for the session via TanStack Query, so it's effectively a once-per-tab cost.
  • No step-up gate: the endpoint surfaces nothing the user doesn't already know about their own account.
Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
{ }

Refresh

Responses

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "csrf_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string",
  • "token_type": "bearer"
}

Reset Password

#43: complete a self-service password reset.

Consumes the dqpr_ token from the emailed link: verifies its hash, that it's unused and unexpired, then rotates the user's bcrypt password, marks the token (and any siblings) used, and REVOKES every outstanding refresh token so all existing sessions are killed — a reset is the canonical "I may have been compromised" action, so nothing the old password minted survives. A bad / expired / already-used token yields an indistinguishable 400 so the endpoint isn't a token-probing oracle.

Request Body schema: application/json
required
new_password
required
string (New Password) >= 8 characters
token
required
string (Token)

Responses

Request samples

Content type
application/json
{
  • "new_password": "stringst",
  • "token": "string"
}

Response samples

Content type
application/json
{
  • "message": "string"
}

Signup

SSO-6: create a new user account.

Two branches:

  • The email's domain is claimed by an existing org and that claim is verified — we create the user but DO NOT mint a session or a new org. Instead we insert a pending JoinRequest row and return SignupPendingApprovalResponse. The UI prompts the user to wait for admin approval.
  • Otherwise the create-org path runs. It provisions the new org's tenant schema through the canonical Alembic routine, then creates the user, the org, the owner membership and the default workspace in one transaction, mints a session and returns LoginResponse. A provisioning failure returns 503 and commits nothing.

Both branches normalise the email lower-case + strip so the case-insensitive uniqueness contract on users.email is honoured.

Rate limits (429 + Retry-After): every attempt counts against a loose per-IP hourly bucket (auth_signup_rate_limit_per_hour). The create-org branch also charges two daily buckets before it does any work, because each org it creates costs a whole tenant schema: a tight per-IP one (auth_signup_org_create_per_ip_rate_limit_per_day), then one global cap (auth_signup_org_create_rate_limit_per_day). A client the per-IP bucket blocks never reaches, and so never charges, the global one. IPv6 clients share the per-IP create-org bucket per /64. If the branch fails after charging and before its commit (503, a lost-race 409, any other error), both create-org hits are refunded. Both branches refuse a duplicate email with 409 — propose-join is a flow for new users; an existing user requesting access to an org they already see should go through the login + invite path.

Request Body schema: application/json
required
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
Org Name (string) or Org Name (null) (Org Name)
password
required
string (Password)

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "full_name": "string",
  • "org_name": "string",
  • "password": "string"
}

Response samples

Content type
application/json
null

Sso Callback

FEATURE-31: IdP-driven callback — see :func:_sso_callback_impl.

C-186-1 (a): the callback is a top-level BROWSER navigation, so no failure may render as JSON in the user's tab. Every error — a refused link, an expired state, an IdP/token problem, an unexpected exception — is logged server-side with its (non-secret) detail, and 303-redirects to /login?sso_error=<code>; the login page maps the code to friendly copy. The codes are enumerated in :data:SSO_ERROR_CODES and carry nothing secret.

POST is registered alongside GET for the SAML HTTP-POST binding (the handler always read SAMLResponse / RelayState from the form, but only GET was routed, so a SAML login 405'd).

path Parameters
org_slug
required
string (Org Slug)

Responses

Response samples

Content type
application/json
null

Sso Callback

FEATURE-31: IdP-driven callback — see :func:_sso_callback_impl.

C-186-1 (a): the callback is a top-level BROWSER navigation, so no failure may render as JSON in the user's tab. Every error — a refused link, an expired state, an IdP/token problem, an unexpected exception — is logged server-side with its (non-secret) detail, and 303-redirects to /login?sso_error=<code>; the login page maps the code to friendly copy. The codes are enumerated in :data:SSO_ERROR_CODES and carry nothing secret.

POST is registered alongside GET for the SAML HTTP-POST binding (the handler always read SAMLResponse / RelayState from the form, but only GET was routed, so a SAML login 405'd).

path Parameters
org_slug
required
string (Org Slug)

Responses

Response samples

Content type
application/json
null

Sso List Providers

SSO-1: list the identity providers configured for org_slug.

Public, unauthenticated. The login page calls this before the user has a session so the SPA can render a picker (or skip straight to the only configured IdP). An attacker who can already guess an org slug can also see whether the slug exists via /sso/{slug}/start, so this endpoint doesn't widen the existing oracle — it just exposes the same set of orgs through a less hostile shape.

Response shape (each row)::

{
  "provider_id": "<uuid>",
  "display_name": "Acme Okta",
  "kind": "oidc" | "saml"
}

Information disclosure (read this before adding fields): only fields that are safe to expose to an unauthenticated viewer go on this response. We deliberately do NOT return:

  • issuer (OIDC) — reveals the customer's IdP host, useful for a targeted-phishing setup.
  • client_id — semi-secret for some IdPs (Azure AD treats it as sensitive; surfacing it pre-auth is a regression).
  • entity_id / sso_url / x509_cert (SAML) — same posture.
  • default_role — the org's RBAC posture is not the public's business.
  • enforced — whether password login is on/off would tell a probing client which orgs to spear-phish via the password path.
  • Anything from config_encrypted — this would defeat HARDEN-16.

Status codes:

  • 200 with an empty list when the slug is known but has no IdPs configured — lets the UI render a clear "no SSO configured" message without leaking the slug-exists/slug-unknown bit twice.
  • 404 when the slug doesn't match any org. We don't enumerate slugs.
path Parameters
org_slug
required
string (Org Slug)

Responses

Response samples

Content type
application/json
null

Sso Start

FEATURE-31: kick off an SSO flow for org_slug.

Returns a JSON body {redirect_url} rather than a 302 so the SPA can drive the navigation (and add tracking / extension state) before handing control to the IdP. The browser then performs the actual redirect.

The state parameter we put on the IdP URL is the FEATURE-31 CSRF defence: a callback with no recognised state is rejected, so a third-party site can't trick a logged-out user into starting a session under an attacker-controlled IdP.

path Parameters
org_slug
required
string (Org Slug)
query Parameters
Provider Id (string) or Provider Id (null) (Provider Id)

Responses

Response samples

Content type
application/json
null

scim

List Groups

List or filter the principal's org's teams.

Supports displayName eq, externalId eq, and id eq. Other attributes fall through to an empty result rather than 400 — same posture as the Users router so an IdP probing for a custom attribute doesn't disable provisioning. Tombstoned teams are excluded everywhere.

Authorizations:
HTTPBearer
query Parameters
Filter (string) or Filter (null) (Filter)
Startindex (integer) or Startindex (null) (Startindex)
Count (integer) or Count (null) (Count)

Responses

Response samples

Content type
application/json
null

Create Group

Create a team from a SCIM Group.

Behaviour matrix:

  • displayName missing → 400.
  • externalId set and a live team already carries it → 409 scimType=uniqueness. Re-pushing an already-provisioned group is the IdP's "I expected this to not exist; reconcile" signal, not "create a duplicate".
  • externalId set and a tombstoned team carries it → restore the row in place: clear deleted_at, sync name/slug, and ADDITIVELY apply any members in the POST body. Pre-DELETE memberships are preserved (SSO-4.1) because Okta's churn cycle (DELETE → POST with empty members → PATCH add) would otherwise wipe them between the POST and the PATCH. Targeted removals after restore happen via PATCH op=remove. Preserves the original team id and audit history (the SSO-4 contract).
  • No externalId (manual SCIM create, rare) → just create a fresh team. Slug collisions are auto-suffixed.
Authorizations:
HTTPBearer
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null

Delete Group

Soft-delete the team.

Sets deleted_at = NOW(); preserves the row, memberships, and workspace grants so a same-externalId recreate restores them intact. Subsequent GETs 404. The first-party Teams API (routers/teams.py) also filters tombstones from every read.

Why not hard delete: the IdP can delete + recreate a group within seconds (Okta's "assignment churn" cycle) and a hard delete would lose the original team id — every audit row pointing at it would dangle, and any admin-added manual memberships would be wiped silently. Soft delete keeps the audit trail intact and makes "restore on recreate" trivial.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get Group

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
null

Patch Group

Apply RFC 7644 §3.5.2 PATCH operations to a team-backed Group.

Supported ops (Okta + Entra IT's actual emit set):

  • replace path=displayName — rename the team.
  • replace path=externalId — update the IdP-side group id.
  • replace path=members — wholesale replace SCIM membership.
  • add path=members — append members (idempotent).
  • remove path=members[value eq "<id>"] — drop one member.
  • remove path=members — clear all SCIM members.

Manual / SSO-JIT membership rows are never touched — that's the added_via='scim' filter inside :func:_reconcile_team_members and the symmetric guard inside the per-row remove.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null

Replace Group

Replace the team's name / externalId / SCIM-stamped membership.

Does NOT touch manual or SSO-JIT membership rows: those are owned by other code paths and surviving a wholesale SCIM PUT is the whole point of the added_via column. displayName and externalId are mutable here; renaming sucks in a new slug but auto-suffixes on collision so a PUT can't 409 on a same-display-name rename inside one org.

Authorizations:
HTTPBearer
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null

List Resource Types

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

List Schemas

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

Get Schema

Authorizations:
HTTPBearer
path Parameters
schema_id
required
string (Schema Id)

Responses

Response samples

Content type
application/json
null

Get Service Provider Config

Authorizations:
HTTPBearer

Responses

Response samples

Content type
application/json
null

List Users

List or filter users in the principal's org.

Okta polls this with filter=userName eq "..." for every assignment decision; the EqualityFilter path keeps that O(1) via the unique index on users.email.

Authorizations:
HTTPBearer
query Parameters
Filter (string) or Filter (null) (Filter)
Startindex (integer) or Startindex (null) (Startindex)
Count (integer) or Count (null) (Count)

Responses

Response samples

Content type
application/json
null

Create User

Provision a user into the principal's org.

Idempotent on email: if a global :class:UserModel row already exists we attach an :class:OrgMembership to it (the user is a member of multiple tenants). If an active membership already exists, returns 409 + scimType=uniqueness (RFC 7644 §3.3) so the IdP marks the provisioning attempt as "already provisioned".

Authorizations:
HTTPBearer
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null

Delete User

Deprovision a user from this org.

We don't delete the global :class:UserModel row — the same person might still belong to other tenants in the deployment. We delete only the membership row (and any workspace memberships hanging off it via cascade-equivalent application code below). Okta treats a 204 as "user deprovisioned"; subsequent GETs return 404.

Authorizations:
HTTPBearer
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "detail": [
    ]
}

Get User

Authorizations:
HTTPBearer
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
null

Patch User

PATCH applies a list of operations (RFC 7644 §3.5.2).

We only implement the patch shapes the Okta + Entra test matrices exercise:

  • replace for active (deprovision / reactivate).
  • replace for name.givenName / familyName / userName.
  • Bare replace with a value object (Okta's default shape).

Anything else returns 400 with scimType=invalidPath; the IdP surface this to the admin who can then either correct the connector mapping or open a ticket against us.

Authorizations:
HTTPBearer
path Parameters
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null

Replace User

PUT replaces the whole user representation (RFC 7644 §3.5.1).

Authorizations:
HTTPBearer
path Parameters
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
property name*
additional property
any

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
null