Skip to main content

DriftWise API (2)

Download OpenAPI specification:Download

DriftWise multi-cloud infrastructure drift detection API. All endpoints live under /api/v2.

admin

List audit log entries

Paginated list of every audit event on the platform. Covers platform events (org.created, domain.added, user.deleted) and org-scoped events. Use GET /orgs/:id/audit-log for per-org filtering.

Authorizations:
OIDCBearer
query Parameters
limit
integer

page size (default 50, max 200)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/audit-log?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Verify the platform audit chain

Walks the hash chain over platform-scoped audit events (domain.added, user.deleted, org.created, etc.). Platform-admin only. Use expected_min_seq to detect tail truncation via an out-of-band watermark.

Authorizations:
OIDCBearer
query Parameters
expected_min_seq
integer

Operator-tracked watermark; broken if current head_seq is lower

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/audit-log/verify?expected_min_seq=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "checked": 0,
  • "head_hash": "string",
  • "head_seq": 0,
  • "status": "string"
}

List allowed email domains

Email domains whose users may authenticate. Users with domains not on this list are rejected at login with 403.

Authorizations:
OIDCBearer

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/admin/domains \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Add an allowed email domain

Validation: must contain a dot, must not contain @, spaces, or a protocol prefix. Automatically lowercased.

Authorizations:
OIDCBearer
Request Body schema: application/json
required

Domain to allow

domain
required
string

Domain is the bare email-domain string (e.g. "acme.com"). Must contain a dot, must not contain @, spaces, or a protocol prefix (http:// / https://). Automatically lowercased server-side.

Responses

Request samples

Content type
application/json
{
  • "domain": "acme.com"
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "domain": "string",
  • "id": "string"
}

Remove an allowed email domain

Users with this domain will be rejected at next login. Existing sessions survive up to 30s (cache TTL) before invalidation.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Domain allowlist entry ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/admin/domains/%7Bid%7D \
  --header 'Authorization: REPLACE_KEY_VALUE'

Report admin status (and bootstrap first admin)

Any authenticated user can call this — unlike every other /admin/* route. Returns is_platform_admin: true once the caller is (or has been just auto-promoted to) a platform admin. Auto-promotion fires once, for the first OIDC caller with a domain-allowed email, when zero admins exist in the platform.

Authorizations:
OIDCBearerAPIKey

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/admin/me \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "is_platform_admin": true
}

Add a user to an org

Creates an org membership with a role. Enforces the org's plan seat limit; over the limit returns 402 with the canonical PaymentRequired body. On Team plans, best-effort Stripe seat-quantity sync fires after creation.

Authorizations:
OIDCBearer
Request Body schema: application/json
required

user + org + role

org_id
required
string
role
required
string
Enum: "owner" "admin" "member" "viewer"

Role must be one of owner, admin, member, viewer. Case-sensitive.

user_id
required
string

Responses

Request samples

Content type
application/json
{
  • "org_id": "b2c3d4e5-6789-01bc-defa-2345678901bc",
  • "role": "member",
  • "user_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
}

Response samples

Content type
application/json
{
  • "acceptedAt": "string",
  • "createdAt": "string",
  • "expiresAt": "string",
  • "id": "string",
  • "invitedBy": "string",
  • "lastActiveAt": "string",
  • "orgID": "string",
  • "role": "string",
  • "updatedAt": "string",
  • "userID": "string"
}

Remove a membership

Revokes the membership. Access to the org stops immediately on the handling pod; other pods invalidate within 30s (cache TTL). Best-effort Stripe seat-quantity sync runs in the background.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Membership ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/admin/memberships/%7Bid%7D \
  --header 'Authorization: REPLACE_KEY_VALUE'

Change a membership's role (admin)

OIDC-only — API keys are rejected with 403 for role mutations, regardless of scope. Same-role requests are idempotent no-ops (200, noop:true, no audit row). Demoting the sole org owner returns 409.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Membership ID (UUID)

Request Body schema: application/json
required

New role

role
required
string
Enum: "owner" "admin" "member" "viewer"

Responses

Request samples

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

Response samples

Content type
application/json
{
  • "membership_id": "string",
  • "noop": true,
  • "org_id": "string",
  • "role": "string",
  • "user_id": "string"
}

List all organizations

Platform-admin-only paginated list of every org. Includes free, team, and enterprise plans. Ordered by created_at descending.

Authorizations:
OIDCBearer
query Parameters
limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/orgs?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Create an organization (admin)

Provision a new org on the Free plan. Does NOT make the caller an owner — use POST /admin/memberships afterward to assign ownership.

Authorizations:
OIDCBearer
Request Body schema: application/json
required

Org slug + display name

display_name
required
string
slug
required
string

Responses

Request samples

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

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "display_name": "string",
  • "features": [
    ],
  • "id": "string",
  • "plan": "string",
  • "slug": "string"
}

List users

Paginated list of every user known to the platform. Includes both admins and regular users; the is_platform_admin flag distinguishes.

Authorizations:
OIDCBearer
query Parameters
limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/users?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Delete a user (soft)

Soft-delete: sets deleted_at and clears the platform admin flag. Org memberships remain. Auth is denied immediately on the handling pod; other pods invalidate within 30s via the revocation store. Cannot delete yourself.

Authorizations:
OIDCBearer
path Parameters
id
required
string

User ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/admin/users/%7Bid%7D \
  --header 'Authorization: REPLACE_KEY_VALUE'

List a user's org memberships

Returns the user's role in every org they belong to. Order unspecified.

Authorizations:
OIDCBearer
path Parameters
id
required
string

User ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/admin/users/%7Bid%7D/memberships \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Get WIF configuration for CI integration

Platform-admin-only (OIDC JWT required). Returns the GCP Workload Identity Federation config external CI systems use to obtain short-lived credentials. Path is under /api/v2/ but outside /admin/ — requires platform admin via the RequirePlatformAdmin middleware pinned on this route specifically.

Authorizations:
OIDCBearer

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/federation-info \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{}

Create a new org (self-service)

OIDC-only — API keys cannot create orgs. Caller becomes the sole owner. X-Org-ID header is rejected with 400 to catch client bugs that assume the header targets an existing org. New orgs start on the Free plan.

Authorizations:
OIDCBearer
Request Body schema: application/json
required

Org slug + display name

display_name
required
string
slug
required
string

Responses

Request samples

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

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "display_name": "string",
  • "features": [
    ],
  • "id": "string",
  • "plan": "string",
  • "slug": "string"
}

Get org metadata

Returns the org record including plan, slug, display name, and the list of active plan feature flags. Features drive frontend UI gating — features.includes('audit_compliance') is load-bearing across the app.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "display_name": "string",
  • "features": [
    ],
  • "id": "string",
  • "plan": "string",
  • "slug": "string"
}

Change a team member's role

Owner-only, OIDC-only. The platform-admin mirror (PATCH /admin/memberships/{id}) permits cross-org role changes; this route is scoped to the caller's org. Self-demotion returns 409 — ask another owner. Same-role requests are idempotent no-ops (200, noop:true, no audit row). Demoting the sole owner returns 409. Cross-org attempts return 404 to avoid an enumeration oracle on membership IDs.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

membership_id
required
string

Membership ID (UUID) — must belong to the org in the URL

Request Body schema: application/json
required

New role

role
required
string
Enum: "owner" "admin" "member" "viewer"

Responses

Request samples

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

Response samples

Content type
application/json
{
  • "membership_id": "string",
  • "noop": true,
  • "org_id": "string",
  • "role": "string",
  • "user_id": "string"
}

audit

List audit log entries

Paginated list of every audit event on the platform. Covers platform events (org.created, domain.added, user.deleted) and org-scoped events. Use GET /orgs/:id/audit-log for per-org filtering.

Authorizations:
OIDCBearer
query Parameters
limit
integer

page size (default 50, max 200)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/audit-log?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Verify the platform audit chain

Walks the hash chain over platform-scoped audit events (domain.added, user.deleted, org.created, etc.). Platform-admin only. Use expected_min_seq to detect tail truncation via an out-of-band watermark.

Authorizations:
OIDCBearer
query Parameters
expected_min_seq
integer

Operator-tracked watermark; broken if current head_seq is lower

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/admin/audit-log/verify?expected_min_seq=SOME_INTEGER_VALUE' \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "checked": 0,
  • "head_hash": "string",
  • "head_seq": 0,
  • "status": "string"
}

List Compliance Packs

Paginated list of the org's Compliance Pack rows in every status (pending, running, done, error). Any org member can list.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

page size (default 50, max 200)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/audit-exports?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Enqueue a Compliance Pack

OIDC-only, owner/admin. Period validated (start<end, end<=now) and clamped to plan retention. Rate-limited at 5/24h per org. Returns 202 with the queued row — poll GET /audit-exports/{eid} for status transition to 'done', then GET /audit-exports/{eid}/download for the ZIP.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Period start + end (UTC)

period_end
string
period_start
string

Responses

Request samples

Content type
application/json
{
  • "period_end": "string",
  • "period_start": "string"
}

Response samples

Content type
application/json
{
  • "artifact_bytes": 0,
  • "artifact_sha256": "string",
  • "created_at": "string",
  • "error_message": "string",
  • "expires_at": "string",
  • "finished_at": "string",
  • "id": "string",
  • "period_end": "string",
  • "period_start": "string",
  • "requested_by": "string",
  • "retry_count": 0,
  • "started_at": "string",
  • "status": "string"
}

Delete a Compliance Pack

OIDC-only, owner/admin. Removes both the DB row and the stored ZIP. Artifact delete is best-effort — bucket lifecycle catches any residue. Idempotent on concurrent deletes (returns 204 even if the row vanished between the read and the delete).

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

eid
required
string

Export ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/audit-exports/%7Beid%7D \
  --header 'Authorization: REPLACE_KEY_VALUE'

Get Compliance Pack status

Returns the row status + metadata. Poll this to wait for async build completion (pending → running → done/error). artifact_key, artifact_bytes, and artifact_sha256 populate only when status=done.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

eid
required
string

Export ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/audit-exports/%7Beid%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "artifact_bytes": 0,
  • "artifact_sha256": "string",
  • "created_at": "string",
  • "error_message": "string",
  • "expires_at": "string",
  • "finished_at": "string",
  • "id": "string",
  • "period_end": "string",
  • "period_start": "string",
  • "requested_by": "string",
  • "retry_count": 0,
  • "started_at": "string",
  • "status": "string"
}

Download Compliance Pack ZIP

OIDC-only, owner/admin. Streams the ZIP with Content-Disposition: attachment and a stable filename derived from the org slug + period. X-Checksum-SHA256 header lets auditors verify integrity without unzipping. 409 when the row exists but is not yet 'done'; 410 when the row is 'done' but the artifact has been GC'd (UI should re-enqueue).

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

eid
required
string

Export ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/audit-exports/%7Beid%7D/download \
  --header 'Authorization: REPLACE_KEY_VALUE'

Verify the org audit chain

Walks the hash chain over the org's audit events and reports intact or broken. Optional expected_min_seq anchor detects tail truncation that in-chain hashes can't (privileged deletes of the latest rows verify clean because the remaining rows still hash correctly). Rate-limited at 10/hour per org.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
expected_min_seq
integer

Caller-tracked watermark; broken if current head_seq is lower

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/audit-log/verify?expected_min_seq=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "checked": 0,
  • "head_hash": "string",
  • "head_seq": 0,
  • "status": "string"
}

policy

List built-in rules

Returns the shipping rule catalog. Optional provider narrows Terraform resource-type lists to one cloud (aws, gcp, azure). Plan-noise rules are matched and filtered similarly.

Authorizations:
OIDCBearerAPIKey
query Parameters
provider
string
Enum: "aws" "gcp" "azure"

Filter types by provider

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/builtin-rules?provider=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "noise_rules": [
    ],
  • "risk_encryption_types": [
    ],
  • "risk_iam_types": [
    ],
  • "risk_network_types": [
    ],
  • "risk_security_types": [
    ],
  • "risk_stateful_types": [
    ]
}

List custom rules

Returns both enabled and disabled rules. Optional rule_type narrows to "noise" or "risk". Not paginated — rule counts are bounded in practice.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
rule_type
string
Enum: "noise" "risk"

Filter by rule type

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/custom-rules?rule_type=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Create a custom rule

Org-defined noise or risk rule. Config is validated against the rule_type schema before save — invalid configs return 400. Requires an authenticated user identity for the audit trail.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Rule definition

config
required
Array of integers
description
required
string
name
required
string
rule_type
required
string
Enum: "noise" "risk"

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "description": "string",
  • "name": "string",
  • "rule_type": "noise"
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "created_at": "string",
  • "created_by": "string",
  • "description": "string",
  • "enabled": true,
  • "id": "string",
  • "name": "string",
  • "rule_type": "string",
  • "updated_at": "string"
}

Delete a custom rule

Hard delete. To temporarily disable a rule without losing config, PATCH with enabled=false instead.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

rule_id
required
string

Custom rule ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/custom-rules/%7Brule_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Get a custom rule

Returns the rule including its config blob and enabled flag.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

rule_id
required
string

Custom rule ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/custom-rules/%7Brule_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "created_at": "string",
  • "created_by": "string",
  • "description": "string",
  • "enabled": true,
  • "id": "string",
  • "name": "string",
  • "rule_type": "string",
  • "updated_at": "string"
}

Toggle a custom rule

Flips the enabled flag. Preserves config so re-enabling later restores the rule exactly as configured.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

rule_id
required
string

Custom rule ID (UUID)

Request Body schema: application/json
required

Desired enabled state

enabled
boolean

Responses

Request samples

Content type
application/json
{
  • "enabled": true
}

Update a custom rule

Replaces name, description, and config. rule_type is immutable — to change a rule's type, delete it and create a new one. Config is revalidated against the existing rule_type.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

rule_id
required
string

Custom rule ID (UUID)

Request Body schema: application/json
required

Updated fields

config
required
Array of integers
description
required
string
name
required
string

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "description": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "created_at": "string",
  • "created_by": "string",
  • "description": "string",
  • "enabled": true,
  • "id": "string",
  • "name": "string",
  • "rule_type": "string",
  • "updated_at": "string"
}

List disabled built-in rules

Returns the org's exception list against the built-in rule catalog. Optional rule_type narrows to "noise" or "risk".

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
rule_type
string
Enum: "noise" "risk"

Filter by rule type

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/disabled-rules?rule_type=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Disable a built-in rule

Suppresses a shipping rule for this org only. Built-in rules themselves are not modified — this creates an org-scoped exception row. Optional reason is free-form; surfaces in audit UI.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Which rule + why

builtin_rule_id
required
string
reason
string
rule_type
required
string
Enum: "noise" "risk"

Responses

Request samples

Content type
application/json
{
  • "builtin_rule_id": "string",
  • "reason": "string",
  • "rule_type": "noise"
}

Response samples

Content type
application/json
{
  • "builtin_rule_id": "string",
  • "created_at": "string",
  • "disabled_by": "string",
  • "id": "string",
  • "reason": "string",
  • "rule_type": "string"
}

Re-enable a built-in rule

Removes the org's exception for this rule, restoring it to active. The disabled-rule row is deleted, not flipped — re-disabling later creates a fresh row.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

disabled_rule_id
required
string

Disabled rule record ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/disabled-rules/%7Bdisabled_rule_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

List plan-noise patterns

Aggregated recurring attributes (noise candidates) across recent scans, bundled with the effective settings that drove the aggregation (window_days, recurrence_threshold). Frontend dashboards render both together. Optional repo_owner/repo_name narrow by originating repo.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
repo_owner
string

Filter by repo owner

repo_name
string

Filter by repo name

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/plan-noise?repo_owner=SOME_STRING_VALUE&repo_name=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "patterns": [
    ],
  • "settings": {
    }
}

Generate fix recommendations

LLM-powered remediation suggestions for a recurring plan-noise pattern. Returns tiered fixes (quick patch, structural refactor, long-term rework) with pros/cons. Same BYOK/platform gate as AnalyzePlan — BYOK bypasses platform quota, platform path reserves and releases on failure.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

fingerprint
required
string

Pattern fingerprint (hash of resource+attribute+action)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/plan-noise/%7Bfingerprint%7D/fix \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "confidence": "high",
  • "fixes": [
    ],
  • "trace_id": "trc-abc123"
}

Get plan-noise settings

Returns recurrence_threshold (minimum occurrences to flag) and window_days (lookback period). Defaults applied server-side when the org has never customized.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/plan-noise/settings \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "recurrence_threshold": 0,
  • "window_days": 0
}

Update plan-noise settings

Replaces recurrence_threshold and window_days. Takes effect on the next ListPlanNoise call — no re-aggregation job triggered.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

New thresholds

recurrence_threshold
required
integer >= 1
window_days
required
integer >= 1

Responses

Request samples

Content type
application/json
{
  • "recurrence_threshold": 1,
  • "window_days": 1
}

Response samples

Content type
application/json
{
  • "recurrence_threshold": 0,
  • "window_days": 0
}

Suppress plan-noise fingerprints

Bulk-creates suppressions. The fingerprints and resource_addresses arrays must be the same length — each index pair produces one suppression. Duration is one of "7d", "30d", "90d", or "forever". Requires an authenticated user identity for the audit trail; API-key calls without user context return 403.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Fingerprints + addresses + duration

duration
required
string
Enum: "7d" "30d" "90d" "forever"
fingerprints
required
Array of strings [ 1 .. 100 ] items
is_false_positive
boolean
reason
required
string
resource_addresses
required
Array of strings [ 1 .. 100 ] items

Responses

Request samples

Content type
application/json
{
  • "duration": "7d",
  • "fingerprints": [
    ],
  • "is_false_positive": true,
  • "reason": "string",
  • "resource_addresses": [
    ]
}

Response samples

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

List plan-noise suppressions

Returns active suppressions. Pass include_expired=true to include suppressions past their expiry (useful for audit). Not paginated.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
include_expired
boolean

Include already-expired suppressions

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/plan-noise/suppressions?include_expired=SOME_BOOLEAN_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Remove a plan-noise suppression

Restores visibility of the suppressed drift. Already-deleted suppressions return 404.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

suppression_id
required
string

Suppression ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/plan-noise/suppressions/%7Bsuppression_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Get risk policy

Returns the attribute-risk policy used by drift classification. An empty policy (version 0, rules: []) is returned when no policy has been saved — callers don't need to handle a 404.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/policy \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "rules": [
    ],
  • "updated_at": "string",
  • "version": 0
}

Replace risk policy

Owner/admin only. Policy is sanitized (lowercased enum values, trimmed whitespace) and validated server-side before save. 422 on validation failure (e.g. unknown severity, malformed glob pattern).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Policy rules + version

Array of objects (github_com_driftwise_backend_internal_policy.PolicyRule)
updated_at
string
version
integer

Responses

Request samples

Content type
application/json
{
  • "rules": [
    ],
  • "updated_at": "string",
  • "version": 0
}

Response samples

Content type
application/json
{
  • "rules": [
    ],
  • "updated_at": "string",
  • "version": 0
}

Get Terraform Registry resource metadata

Fetches documentation + argument/attribute schemas from registry.terraform.io for one (provider, resource_type) pair. Redis-backed cache; rate-limited at 120/min per user. Response shape mirrors registry's native schema.

Authorizations:
OIDCBearerAPIKey
query Parameters
provider
required
string
Enum: "aws" "gcp" "azure"

Cloud provider

resource_type
required
string

Terraform resource type (e.g. aws_s3_bucket)

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/tf-registry/meta?provider=SOME_STRING_VALUE&resource_type=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "docs_url": "string",
  • "fetched_at": "string",
  • "latest_version": "string",
  • "min_version": "string",
  • "provider": "string",
  • "resource_type": "string"
}

llm

List supported LLM providers

Returns the compiled-in LLM provider enums (anthropic, openai, bedrock, gemini, azure_openai). Response is static per server build; frontend caches indefinitely.

Authorizations:
OIDCBearerAPIKey

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/llm-providers \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Delete BYOK LLM configuration

Hard delete. Subsequent LLM calls fall back to the platform default + its quota gate (weekly/hourly). 404 if no row exists.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/llm-config \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Get BYOK LLM configuration

Returns only metadata — provider name + timestamps. Credentials never exposed: the raw api_key / aws_secret_access_key / azure_api_key are write-only.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/llm-config \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "configured_at": "string",
  • "last_validated_at": "string",
  • "provider": "string"
}

Replace BYOK LLM configuration

Strict decoder rejects unknown fields with 400. Credentials are envelope-encrypted before DB write and wiped from memory after. Validation rejects "hosted mode" variants: Anthropic requires api_key, Bedrock requires explicit access key + region (not instance profile — the profile would attach to DriftWise's SA, not the customer's).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

BYOK credentials

api_key
string

Anthropic / OpenAI / Gemini

aws_access_key_id
string
aws_region
string

AWS Bedrock

aws_role_arn
string
aws_secret_access_key
string
aws_session_token
string
azure_api_key
string
azure_api_version
string
azure_deployment
string
azure_endpoint
string

Azure OpenAI

model
string
provider
string

Responses

Request samples

Content type
application/json
{
  • "api_key": "string",
  • "aws_access_key_id": "string",
  • "aws_region": "string",
  • "aws_role_arn": "string",
  • "aws_secret_access_key": "string",
  • "aws_session_token": "string",
  • "azure_api_key": "string",
  • "azure_api_version": "string",
  • "azure_deployment": "string",
  • "azure_endpoint": "string",
  • "model": "string",
  • "provider": "string"
}

Response samples

Content type
application/json
{
  • "configured_at": "string",
  • "last_validated_at": "string",
  • "provider": "string"
}

Get LLM usage + caps

Returns current-bucket used/cap for both the weekly and hourly gates, plus the next reset timestamp for each. byok_configured=true means the caps are advisory: BYOK calls bypass both gates. Caps of -1 mean unlimited.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/llm-usage \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "byok_configured": true,
  • "hour_resets_at": "string",
  • "hourly_cap": 0,
  • "hourly_used": 0,
  • "week_resets_at": "string",
  • "weekly_cap": 0,
  • "weekly_used": 0
}

accounts

Ingest K8s operator resource report

API-key-only endpoint for the in-cluster DriftWise operator. Creates a scan_run with scan_type=operator and bulk-upserts live_resources. K8s resources skip cloud enrichment (operator sends full properties inline). OIDC callers are rejected with 403 — this is a machine-to-machine path.

Authorizations:
APIKey
Request Body schema: application/json
required

Cluster + namespace + resources

cluster_id
required
string
custom_prompt
string
generate_iac
boolean
iac_format
string
namespace
required
string
required
Array of objects (github_com_driftwise_backend_internal_cloud.Resource)

Responses

Request samples

Content type
application/json
{
  • "cluster_id": "string",
  • "custom_prompt": "string",
  • "generate_iac": true,
  • "iac_format": "string",
  • "namespace": "string",
  • "resources": [
    ]
}

Response samples

Content type
application/json
{
  • "report_id": "string",
  • "resource_count": 0
}

List cloud accounts

Paginated list of registered cloud accounts. Credentials are redacted — callers see only metadata (display name, provider, external account ID, credential type).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/accounts?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "accounts": [
    ],
  • "limit": 100,
  • "offset": 0,
  • "total": 3
}

Register a cloud account

Validates credentials against the cloud provider before saving. Credentials are envelope-encrypted at rest (AES-256-GCM); the DB only sees ciphertext. Account-ID spoofing defense: the provider-reported identity must match the caller's external_account_id. Returns 402 when the org's plan cloud-account limit is reached.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Cloud account config

credential_ref
required
string
credential_type
required
string
default_region
string
display_name
required
string
external_account_id
required
string
provider
required
string

Responses

Request samples

Content type
application/json
{
  • "credential_ref": "string",
  • "credential_type": "string",
  • "default_region": "string",
  • "display_name": "string",
  • "external_account_id": "string",
  • "provider": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "credential_type": "string",
  • "default_region": "string",
  • "display_name": "string",
  • "external_account_id": "string",
  • "id": "string",
  • "last_validated_at": "string",
  • "org_id": "string",
  • "provider": "string"
}

Cloud-account onboarding templates

Returns trust-policy / IAM-grant / federated-credential templates for the given provider, with the caller's org UUID + deployment values baked in. Customer pastes the result into their cloud account, then registers the cloud account via POST /orgs/:id/accounts.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
provider
required
string

Provider: aws | gcp | azure

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/cloud-onboarding-info?provider=SOME_STRING_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "az_cli_command": "string",
  • "federated_credential": {
    },
  • "iam_grant_command": "string",
  • "oidc_provider_create_command": "string",
  • "org_id": "00000000-0000-0000-0000-000000000abc",
  • "provider": "aws",
  • "trust_policy": {
    },
  • "wif_principal": "string"
}

List live resources

Paginated list of cloud resources discovered by recent scans. Optional account_id narrows to one cloud account.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
account_id
string

Filter by cloud account ID

provider
string
Enum: "aws" "azure" "gcp"

Filter by cloud provider

limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/resources?account_id=SOME_STRING_VALUE&provider=SOME_STRING_VALUE&limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "limit": 0,
  • "offset": 0,
  • "resources": [
    ],
  • "total": 0
}

Get one live resource

Fetches a single live_resource row by ID, including enrichment status and properties when present.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

resource_id
required
string

Live resource ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/resources/%7Bresource_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "enrichment_failure_reason": "string",
  • "enrichment_status": "string",
  • "iac_resource_id": "string",
  • "id": "string",
  • "last_seen_at": "string",
  • "name": "string",
  • "normalized_type": "string",
  • "properties": [
    ],
  • "provider": "string",
  • "provider_resource_id": "string",
  • "provider_type": "string",
  • "region": "string",
  • "status": "string"
}

Enrich one resource

Fetches full resource properties from the cloud API on demand. Skips cleanly for resources already at enrichment_status=enriched or n/a. Returns 422 when the enricher reports a per-resource failure (e.g. permission denied, resource gone) — the resource row is marked failed and the reason is returned.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

resource_id
required
string

Live resource ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/resources/%7Bresource_id%7D/enrich \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "enrichment_failure_reason": "string",
  • "enrichment_status": "string",
  • "iac_resource_id": "string",
  • "id": "string",
  • "last_seen_at": "string",
  • "name": "string",
  • "normalized_type": "string",
  • "properties": [
    ],
  • "provider": "string",
  • "provider_resource_id": "string",
  • "provider_type": "string",
  • "region": "string",
  • "status": "string"
}

List supported cloud providers

Returns descriptors for every registered cloud provider (AWS, Azure, GCP + any future additions). Also serves as the frontend's auth-probe target after OIDC login — a 401/403 triggers the "access denied" sign-out flow.

Authorizations:
OIDCBearerAPIKey

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/providers \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

drift

Analyze a Terraform plan

LLM-powered plan analysis. Returns a risk level, structured change summary, and human-readable narrative. BYOK LLM config (if present for the org) takes precedence over the platform default; transient BYOK failures fail loud rather than silently falling back (the "your data, your perimeter" contract). Non-BYOK callers consume platform quota via PlatformGate — 402 on plan exhaustion, 429 on hourly throttle.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Terraform plan JSON + optional CI metadata

object (internal_api.CIMetadataRequest)
plan_json
required
string

Responses

Request samples

Content type
application/json
{
  • "ci": {
    },
  • "plan_json": "string"
}

Response samples

Content type
application/json
{
  • "changes": [
    ],
  • "narrative": "string",
  • "plan_noise": {
    },
  • "risk_level": "string",
  • "scan_run": {
    },
  • "summary": [
    ]
}

Get posture summary

Per-account coverage percentage, risk level, and undeclared-resource count aggregated over the latest scan/drift data. Drives the dashboard's posture card.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/posture \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "accounts": [
    ],
  • "org_id": "string"
}

Get drift results for a scan

Returns the drift snapshot (new, changed, missing items) produced by the drift worker. Returns 404 if drift hasn't been computed yet — call POST /scans/{scan_id}/drift/compute first to produce it.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D/drift \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "changed_count": 0,
  • "coverage_pct": 0,
  • "created_at": "string",
  • "extra_count": 0,
  • "id": "string",
  • "items": [
    ],
  • "matched_count": 0,
  • "missing_count": 0,
  • "narrative": "string",
  • "narrative_status": "string",
  • "parse_failure_count": 0,
  • "risk_level": "string",
  • "scan_run_id": "string",
  • "total_iac": 0,
  • "total_live": 0
}

Compute drift for a scan (sync)

Runs drift computation inline against the scan's live+IaC data and returns the produced snapshot. Requires scan to be status=done. Takes seconds for small scans, tens of seconds for large ones — the companion async drift-worker process handles the same computation for scheduled workflows.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D/drift/compute \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "changed_count": 0,
  • "coverage_pct": 0,
  • "created_at": "string",
  • "extra_count": 0,
  • "id": "string",
  • "items": [
    ],
  • "matched_count": 0,
  • "missing_count": 0,
  • "narrative": "string",
  • "narrative_status": "string",
  • "parse_failure_count": 0,
  • "risk_level": "string",
  • "scan_run_id": "string",
  • "total_iac": 0,
  • "total_live": 0
}

api-keys

List API keys

Paginated list of API keys. The raw_key field is always empty — the raw key is shown only once at creation time (POST /api-keys). Callers see key IDs, names, prefixes, scopes, and creation timestamps.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/api-keys?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "api_keys": [
    ],
  • "limit": 100,
  • "offset": 0,
  • "total": 2
}

Create an API key

Returns the raw key once in the response raw_key field — never retrievable afterward. Scopes are validated at create time (typos like "admin" instead of "write" fail fast). Returns 402 when the org's plan API key limit is reached.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Key name + scopes

name
required
string
scopes
Array of strings

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "scopes": [
    ]
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "id": "string",
  • "key_prefix": "string",
  • "name": "string",
  • "raw_key": "string",
  • "scopes": [
    ]
}

Revoke an API key

OIDC-only — API keys cannot revoke other API keys (lateral-movement defense). Revocation propagates across backend pods within one Redis round-trip via the revocation store; on Redis failure falls back to the 30s cache TTL floor. Already-revoked keys return 404 — idempotent from the caller's perspective.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

keyID
required
string

API key ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/api-keys/%7BkeyID%7D \
  --header 'Authorization: REPLACE_KEY_VALUE'

billing

Create Stripe checkout session

OIDC-only. Provisions a Stripe customer on first call (stored in org.stripe_customer_id). Returns the hosted Stripe Checkout URL. Frontend redirects into this URL; Stripe's return URL points back to the app, and webhook events finalize the plan change.

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Billing interval

interval
required
string
Enum: "month" "year"

Interval selects the Stripe price: "month" for monthly billing, "year" for annual. Anything else returns 400.

Responses

Request samples

Content type
application/json
{
  • "interval": "month"
}

Response samples

Content type
application/json

Create Stripe customer portal session

OIDC-only. Returns the hosted Stripe Customer Portal URL where the caller can manage their subscription, view invoices, update payment methods, and cancel. Requires an existing Stripe customer — a 400 is returned if the org has no Stripe account yet (upgrade via checkout first).

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/billing/portal \
  --header 'Authorization: REPLACE_KEY_VALUE'

Response samples

Content type
application/json

Get current billing usage

Reads from the database only — no Stripe round-trip. Available even when STRIPE_SECRET_KEY is unset. Seat count is limit-aware; -1 in seats.included means unlimited. API-key callers always see role "admin" regardless of the key's scope. Platform-LLM analysis counters live on the dedicated /llm-usage endpoint.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/billing/usage \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "features": [
    ],
  • "plan": "team",
  • "role": "owner",
  • "seats": {
    },
  • "subscription_status": "active"
}

webhooks

List GitHub installations

Returns up to limit linked installations (default 100, max 500). Unlike most list endpoints, this is limit-capped rather than paginated — orgs typically have <10 installations.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

max installations to return (default 100, max 500)

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/github-installations?limit=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Link a GitHub App installation

Called from the GitHub App post-install redirect to associate the installation with this DriftWise org. installation_id comes from GitHub; account_login is the GitHub org/user that installed the App.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

GitHub installation ID + account login

account_login
required
string
installation_id
required
integer

Responses

Request samples

Content type
application/json
{
  • "account_login": "string",
  • "installation_id": 0
}

Response samples

Content type
application/json
{
  • "account_login": "string",
  • "created_at": "string",
  • "id": "string",
  • "installation_id": 0,
  • "updated_at": "string"
}

Unlink a GitHub installation

Removes the DriftWise-side record. Does not uninstall the App from GitHub — the user must do that separately via GitHub's UI.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

install_id
required
string

Installation link ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/github-installations/%7Binstall_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

List webhook configs

Paginated list. raw_secret is always empty — shown only once at creation. Optional provider filter narrows by GitLab/Atlantis.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
provider
string
Enum: "gitlab" "atlantis"

Filter by provider

limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/webhook-configs?provider=SOME_STRING_VALUE&limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "limit": 100,
  • "offset": 0,
  • "total": 3,
  • "webhook_configs": [
    ]
}

Register a webhook config

Generates a whsec_-prefixed secret (32 random bytes, hex) returned once in the response. GitLab tokens are live-validated against the target instance — expired tokens, wrong scope, or unreachable endpoints fail at save time with a specific 400 message. provider_base_url is SSRF-validated (must be public HTTPS).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Webhook config

api_token
string
label
required
string
provider
required
string
provider_base_url
string
repo_path
string

Responses

Request samples

Content type
application/json
{
  • "api_token": "string",
  • "label": "string",
  • "provider": "string",
  • "provider_base_url": "string",
  • "repo_path": "string"
}

Response samples

Content type
application/json
{
  • "api_token_expires_at": "string",
  • "api_token_prefix": "string",
  • "api_token_status": "active",
  • "created_at": "string",
  • "enabled": true,
  • "id": "string",
  • "label": "string",
  • "provider": "string",
  • "provider_base_url": "string",
  • "raw_secret": "string",
  • "repo_path": "string",
  • "secret_prefix": "string",
  • "updated_at": "string"
}

Delete a webhook config

Removes the config and stops accepting webhooks signed with its secret. Already-deleted configs return 404.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

config_id
required
string

Webhook config ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/webhook-configs/%7Bconfig_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Update a webhook config

Toggle enabled state and/or rotate the api_token. At least one field required — empty body is a caller bug. Token rotations re-validate against GitLab before save; failures return 400 with an actionable message and never contain the raw token.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

config_id
required
string

Webhook config ID (UUID)

Request Body schema: application/json
required

Fields to update

api_token
string

non-empty to replace; empty string clears the token

enabled
boolean

Responses

Request samples

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

Response samples

Content type
application/json
{
  • "api_token_expires_at": "string",
  • "api_token_prefix": "string",
  • "api_token_status": "active",
  • "created_at": "string",
  • "enabled": true,
  • "id": "string",
  • "label": "string",
  • "provider": "string",
  • "provider_base_url": "string",
  • "raw_secret": "string",
  • "repo_path": "string",
  • "secret_prefix": "string",
  • "updated_at": "string"
}

infrastructure

Provider accounts

List cloud accounts for a provider with resource counts and drift posture.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

provider
required
string

Cloud provider (aws, gcp, azure)

query Parameters
limit
integer

Page size (default 100, max 500)

offset
integer

Page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/infrastructure/%7Bprovider%7D/accounts?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "accounts": [
    ],
  • "total": 0
}

Drift history

Daily-bucketed drift trend data for charts. Filter by provider, account, or both.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
provider
string

Filter by provider

account_id
string

Filter by account ID

days
integer

Lookback window (default 30, max 90)

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/infrastructure/drift-history?provider=SOME_STRING_VALUE&account_id=SOME_STRING_VALUE&days=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Infrastructure summary

Per-provider rollup of accounts, resources, drift, and risk.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/infrastructure/summary \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Resource plan links

Plan analyses that referenced this resource, with CI metadata.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

resource_id
required
string

Resource ID (UUID)

query Parameters
limit
integer

Page size (default 20, max 100)

offset
integer

Page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/resources/%7Bresource_id%7D/plan-links?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "links": [
    ],
  • "total": 0
}

scans

List scan runs

Paginated scan history, filtered by retention window (free: 24h, team+: unlimited). Optional account_id and scan_type narrow results.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
account_id
string

Filter by cloud account ID

scan_type
string
Enum: "manual" "scheduled" "webhook"

Filter by scan type

provider
string
Enum: "aws" "azure" "gcp"

Filter by cloud provider

limit
integer

page size (default 50, max 200)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans?account_id=SOME_STRING_VALUE&scan_type=SOME_STRING_VALUE&provider=SOME_STRING_VALUE&limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "limit": 50,
  • "offset": 0,
  • "scans": [
    ],
  • "total": 42
}

Queue a new scan run

Creates a scan_run row with status=pending and returns immediately with 202. A background worker picks it up and transitions it through running → done (or error). Poll GET /scans/{scan_id} to track progress.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Scan config: cloud account, region filters, CI metadata

object (internal_api.CIMetadataRequest)
cloud_account_id
required
string
filter_regions
Array of strings

Responses

Request samples

Content type
application/json
{
  • "ci": {
    },
  • "cloud_account_id": "string",
  • "filter_regions": [
    ]
}

Response samples

Content type
application/json
{
  • "branch": "string",
  • "changed_count": 0,
  • "cloud_account_id": "string",
  • "commit_sha": "string",
  • "created_at": "string",
  • "drift_status": "string",
  • "finished_at": "string",
  • "id": "string",
  • "missing_count": 0,
  • "new_count": 0,
  • "org_id": "string",
  • "pr_number": 0,
  • "provider": "string",
  • "repo_name": "string",
  • "repo_owner": "string",
  • "repo_url": "string",
  • "resource_count": 0,
  • "result_json": [
    ],
  • "scan_errors": [
    ],
  • "scan_type": "string",
  • "started_at": "string",
  • "status": "string",
  • "status_kind": "string"
}

Get a scan run

Returns current state including status, resource counts, scan_errors, and derived status_kind (ok/partial/failed). drift_status is non-null only after drift computation has run.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "branch": "string",
  • "changed_count": 0,
  • "cloud_account_id": "string",
  • "commit_sha": "string",
  • "created_at": "string",
  • "drift_status": "string",
  • "finished_at": "string",
  • "id": "string",
  • "missing_count": 0,
  • "new_count": 0,
  • "org_id": "string",
  • "pr_number": 0,
  • "provider": "string",
  • "repo_name": "string",
  • "repo_owner": "string",
  • "repo_url": "string",
  • "resource_count": 0,
  • "result_json": [
    ],
  • "scan_errors": [
    ],
  • "scan_type": "string",
  • "started_at": "string",
  • "status": "string",
  • "status_kind": "string"
}

Get drift results for a scan

Returns the drift snapshot (new, changed, missing items) produced by the drift worker. Returns 404 if drift hasn't been computed yet — call POST /scans/{scan_id}/drift/compute first to produce it.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D/drift \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "changed_count": 0,
  • "coverage_pct": 0,
  • "created_at": "string",
  • "extra_count": 0,
  • "id": "string",
  • "items": [
    ],
  • "matched_count": 0,
  • "missing_count": 0,
  • "narrative": "string",
  • "narrative_status": "string",
  • "parse_failure_count": 0,
  • "risk_level": "string",
  • "scan_run_id": "string",
  • "total_iac": 0,
  • "total_live": 0
}

Compute drift for a scan (sync)

Runs drift computation inline against the scan's live+IaC data and returns the produced snapshot. Requires scan to be status=done. Takes seconds for small scans, tens of seconds for large ones — the companion async drift-worker process handles the same computation for scheduled workflows.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D/drift/compute \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "changed_count": 0,
  • "coverage_pct": 0,
  • "created_at": "string",
  • "extra_count": 0,
  • "id": "string",
  • "items": [
    ],
  • "matched_count": 0,
  • "missing_count": 0,
  • "narrative": "string",
  • "narrative_status": "string",
  • "parse_failure_count": 0,
  • "risk_level": "string",
  • "scan_run_id": "string",
  • "total_iac": 0,
  • "total_live": 0
}

List LLM traces for a scan

Returns summary metadata for every LLM trace produced while processing the scan — narrative generation, drift classification, plan-noise fix, etc. Full trace bodies (prompt, response, tokens) available via GET /traces/{trace_id}.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

scan_id
required
string

Scan run ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/scans/%7Bscan_id%7D/traces \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Queue bulk scans

Creates scan runs for every cloud account in the request (empty account_ids = all accounts). Returns 402 if creating these scans would push the org over its concurrent-scan plan limit. Rate-limited at 5/min per org.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Account selection + region filters

account_ids
Array of strings

nil/empty = all accounts

filter_regions
Array of strings

Responses

Request samples

Content type
application/json
{
  • "account_ids": [
    ],
  • "filter_regions": [
    ]
}

Response samples

Content type
application/json
{
  • "count": 0,
  • "scan_ids": [
    ]
}

List scheduled scans

Paginated list. Disabled schedules (by user or auto-disable after too many consecutive failures) are included — check the enabled and disabled_reason fields.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

page size (default 50, max 200)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/schedules?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "limit": 50,
  • "offset": 0,
  • "schedules": [
    ],
  • "total": 3
}

Create a scheduled scan

Cron in 5-field format (minute hour dom month dow). cloud_account_id null = scan all accounts on each run. Plan limits enforce both count and minimum interval — 402 responses differ between "too many schedules" (plan_limit_exceeded) and "too frequent" (plan_schedule_frequency).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Schedule config

cloud_account_id
string

nil = all accounts

enabled
boolean

default true

filter_regions
Array of strings
name
required
string
notify_slack_channel
string
notify_webhook_config_ids
Array of strings
schedule
required
string

Responses

Request samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "enabled": true,
  • "filter_regions": [
    ],
  • "name": "string",
  • "notify_slack_channel": "string",
  • "notify_webhook_config_ids": [
    ],
  • "schedule": "string"
}

Response samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "consecutive_failures": 0,
  • "created_at": "string",
  • "created_by": "string",
  • "disabled_reason": "string",
  • "enabled": true,
  • "filter_regions": [
    ],
  • "id": "string",
  • "last_error": "string",
  • "last_run_at": "string",
  • "name": "string",
  • "next_run_at": "string",
  • "notify_slack_channel": "string",
  • "notify_webhook_config_ids": [
    ],
  • "org_id": "string",
  • "schedule": "string",
  • "updated_at": "string"
}

Delete a scheduled scan

Soft delete — the schedule no longer fires and disappears from list/get, but the row persists for audit history. Not recoverable via API; contact support if a delete was unintentional.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

sid
required
string

Schedule ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/schedules/%7Bsid%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Get a scheduled scan

Returns the full record including last_run_at, last_error, consecutive_failures, and next_run_at. disabled_reason indicates why the scheduler turned it off if enabled=false.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

sid
required
string

Schedule ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/schedules/%7Bsid%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "consecutive_failures": 0,
  • "created_at": "string",
  • "created_by": "string",
  • "disabled_reason": "string",
  • "enabled": true,
  • "filter_regions": [
    ],
  • "id": "string",
  • "last_error": "string",
  • "last_run_at": "string",
  • "name": "string",
  • "next_run_at": "string",
  • "notify_slack_channel": "string",
  • "notify_webhook_config_ids": [
    ],
  • "org_id": "string",
  • "schedule": "string",
  • "updated_at": "string"
}

Update a scheduled scan

PUT with partial semantics: only fields set in the body are updated. Schedule changes revalidate cron expression + minimum-interval plan limit and recompute next_run_at inside the same transaction (TOCTOU-free).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

sid
required
string

Schedule ID (UUID)

Request Body schema: application/json
required

Fields to update

cloud_account_id
string
enabled
boolean
filter_regions
Array of strings
name
string
notify_slack_channel
string
notify_webhook_config_ids
Array of strings
schedule
string

Responses

Request samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "enabled": true,
  • "filter_regions": [
    ],
  • "name": "string",
  • "notify_slack_channel": "string",
  • "notify_webhook_config_ids": [
    ],
  • "schedule": "string"
}

Response samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "consecutive_failures": 0,
  • "created_at": "string",
  • "created_by": "string",
  • "disabled_reason": "string",
  • "enabled": true,
  • "filter_regions": [
    ],
  • "id": "string",
  • "last_error": "string",
  • "last_run_at": "string",
  • "name": "string",
  • "next_run_at": "string",
  • "notify_slack_channel": "string",
  • "notify_webhook_config_ids": [
    ],
  • "org_id": "string",
  • "schedule": "string",
  • "updated_at": "string"
}

Run a scheduled scan now

Fires the schedule immediately, independent of its cron. Uses the schedule's current cloud_account_id + filter_regions. Does not advance next_run_at; the next cron firing happens on schedule. Concurrent-scan plan limit applies.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

sid
required
string

Schedule ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/schedules/%7Bsid%7D/run \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "count": 0,
  • "scan_ids": [
    ]
}

Get an LLM trace

Returns the full trace body. Non-admin callers get a projected view with prompts and raw response redacted; platform admins see the full trace. Status reflects storage state — pending (202) while the drift-worker is still uploading, ready/failed (200) for terminal states, expired (410) past the retention window.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

trace_id
required
string

Trace ID (starts with 'trc-')

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/traces/%7Btrace_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "cached": true,
  • "created_at": "string",
  • "error": "string",
  • "fallback": true,
  • "id": "string",
  • "input_tokens": 0,
  • "inputs": [
    ],
  • "kind": "string",
  • "latency_ms": 0,
  • "model": "string",
  • "org_id": "string",
  • "output_tokens": 0,
  • "parsed": [
    ],
  • "raw_response": "string",
  • "scan_run_id": "string",
  • "system_prompt": "string",
  • "user_prompt": "string"
}

slack

Start Slack OAuth flow

Returns a Slack authorization URL the frontend redirects the browser to. The state parameter is HMAC-signed with the server's encryption key and bound to (org_id, user_id); the callback validates both. Requires the Slack feature flag on the org's plan.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/slack/install \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json

Get Slack installation status

installed=true means the org has a valid Slack token. available=false indicates the DriftWise deployment itself has no Slack app configured — the integration can't be used at all. Team metadata is only populated when installed=true.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/slack/status \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "available": true,
  • "created_at": "string",
  • "installed": true,
  • "scopes": [
    ],
  • "team_id": "string",
  • "team_name": "string"
}

Uninstall Slack integration

Best-effort revokes the bot token at Slack, then deletes the local installation row. Revoke failures are logged but don't fail the endpoint — the local delete is the authoritative state.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/slack/uninstall \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "uninstalled": true
}

Slack OAuth callback (redirect)

Browser-facing callback. NOT called by API clients — Slack's OAuth redirect lands here. Happy path returns 302 to /#/settings?slack=connected or ?slack=denied. Rate-limited at 10/min per IP.

query Parameters
state
required
string

HMAC-signed state produced by POST /slack/install

code
string

OAuth authorization code (absent on user-denial redirects)

error
string

OAuth error param (present on user denial)

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/slack/callback?state=SOME_STRING_VALUE&code=SOME_STRING_VALUE&error=SOME_STRING_VALUE'

Response samples

Content type
application/json
{
  • "error": "resource not found",
  • "error_class": "access_denied",
  • "request_id": "req_01HXABC..."
}

sso

Get SSO (SAML) configuration

Reads from Casdoor. Returns enabled=false (not 404) when no provider exists. scim_endpoint is populated only for plans with the SCIM feature (enterprise tier).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/sso-config \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "enabled": true,
  • "idp_entity_id": "string",
  • "idp_metadata_url": "string",
  • "scim_endpoint": "string"
}

Update SSO (SAML) configuration

OIDC-only. Writes the SAML provider metadata URL + entity ID to Casdoor. Audited with entity_id (safe public identifier); metadata_url is excluded from audit detail (may contain credentials/signed query strings).

Authorizations:
OIDCBearer
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

IdP metadata URL + entity ID

idp_entity_id
string
idp_metadata_url
string

Responses

Request samples

Content type
application/json
{
  • "idp_entity_id": "string",
  • "idp_metadata_url": "string"
}

Response samples

Content type
application/json
{
  • "ok": true
}

state-source-globs

List state-source globs

Returns every non-deleted glob in the org with its current child count.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Responses

Request samples

curl --request GET \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-source-globs \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
[
  • {
    }
]

Create a state-source glob (auto-discovery)

Lists the bucket, applies the .tfstate filter, and creates one glob row plus N child tf_state_sources rows in a single transaction. Up to 500 matches added in alphabetical order; excess returns 201 with truncated: true. Returns 402 when the plan does not include glob_state_sources or when the would-be child count exceeds max_state_sources. Each discovered file counts individually toward the plan limit.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Glob request body

bucket
required
string
cloud_account_id
required
string
display_name
required
string
prefix
string
provider
required
string
region
string
storage_account
string

Responses

Request samples

Content type
application/json
{
  • "bucket": "string",
  • "cloud_account_id": "string",
  • "display_name": "string",
  • "prefix": "string",
  • "provider": "string",
  • "region": "string",
  • "storage_account": "string"
}

Response samples

Content type
application/json
{
  • "added": 0,
  • "glob": {
    },
  • "total_matches": 0,
  • "truncated": true
}

Delete a state-source glob (cascade)

Soft-deletes the glob AND cascade-soft-deletes every child tf_state_sources row created by it. Hand-added sources are unaffected. The audit log records the child count at delete time.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

glob_id
required
string

Glob ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-source-globs/%7Bglob_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "error": "resource not found",
  • "error_class": "access_denied",
  • "request_id": "req_01HXABC..."
}

Refresh every child of a state-source glob

Asynchronously kicks off a refresh for each non-deleted child tf_state_sources row owned by this glob. Returns 202 with the queued count immediately; per-child status appears via the existing last_fetched_at and last_fetch_error fields on subsequent GET /state-sources. Bounded by GLOB_REFRESH_CONCURRENCY (default 4) so a 50-child glob finishes in roughly 12 sequential refreshes worth of wallclock.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

glob_id
required
string

Glob ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-source-globs/%7Bglob_id%7D/refresh-children \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

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

Rescan a state-source glob

Re-LISTs the bucket and inserts new tf_state_sources rows for any keys that didn't already exist as children. Existing children are untouched. Acquires a per-glob advisory lock so concurrent rescans serialize. Returns 402 if the new-key count would exceed remaining plan budget.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

glob_id
required
string

Glob ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-source-globs/%7Bglob_id%7D/rescan \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "added": 0,
  • "total": 0,
  • "truncated": true
}

Preview a state-source glob

Lists the bucket, applies the .tfstate filter, and returns how many sources would be created vs. how many fit within the current plan limit. Side-effect-free; safe to call repeatedly. Requires the glob_state_sources plan feature (Team or Enterprise).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

Glob request body

bucket
required
string
cloud_account_id
required
string
display_name
required
string
prefix
string
provider
required
string
region
string
storage_account
string

Responses

Request samples

Content type
application/json
{
  • "bucket": "string",
  • "cloud_account_id": "string",
  • "display_name": "string",
  • "prefix": "string",
  • "provider": "string",
  • "region": "string",
  • "storage_account": "string"
}

Response samples

Content type
application/json
{
  • "plan_remaining": 0,
  • "total_matches": 0,
  • "would_add": 0,
  • "would_exceed_plan": true,
  • "would_truncate": true
}

state-sources

List Terraform state sources

Paginated list of state sources. Config blobs are returned verbatim — sensitive fields like TFC tokens are not redacted (stored in DB as plaintext under the org isolation boundary).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

query Parameters
limit
integer

page size (default 100, max 500)

offset
integer

page offset

Responses

Request samples

curl --request GET \
  --url 'https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-sources?limit=SOME_INTEGER_VALUE&offset=SOME_INTEGER_VALUE' \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "limit": 100,
  • "offset": 0,
  • "state_sources": [
    ],
  • "total": 2
}

Register a Terraform state source

Config is kind-specific: gcs (bucket, object), s3 (bucket, key, region), azure_blob (account, container, blob), upload (manual uploads, no backing store), tfc (endpoint, workspace, token). URL-shaped keys in config are SSRF-validated before save. Returns 402 when the org's plan state-source limit is reached.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

Request Body schema: application/json
required

State source config

cloud_account_id
string
config
required
Array of integers
display_name
required
string
kind
required
string

Responses

Request samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "config": [
    ],
  • "display_name": "string",
  • "kind": "string"
}

Response samples

Content type
application/json
{
  • "cloud_account_id": "string",
  • "config": [
    ],
  • "created_at": "string",
  • "display_name": "string",
  • "id": "string",
  • "kind": "string",
  • "last_fetch_error": "string",
  • "last_fetched_at": "string",
  • "latest_snapshot_id": "string",
  • "missing_since": "string",
  • "org_id": "string",
  • "parent_glob_id": "string",
  • "workspaces": [
    ]
}

Delete a Terraform state source

Soft-deletes the state source (sets deleted_at). Historical snapshots + iac_resources remain for audit but are no longer used by drift computation. 404 if the source doesn't exist in this org.

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

src_id
required
string

State Source ID (UUID)

Responses

Request samples

curl --request DELETE \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-sources/%7Bsrc_id%7D \
  --header 'X-API-Key: REPLACE_KEY_VALUE'

Response samples

Content type
application/json
{
  • "error": "resource not found",
  • "error_class": "access_denied",
  • "request_id": "req_01HXABC..."
}

Refresh a Terraform state source

Force-fetches the state bytes from the remote backend (bypassing the 5-minute staleness TTL), parses them, and ingests the resulting iac_resources rows. Synchronous; returns the new or cached snapshot_id. 404 if the source doesn't exist in this org. 502 on remote-backend errors (the last_fetch_error field on GET /state-sources captures the latest reason).

Authorizations:
OIDCBearerAPIKey
path Parameters
id
required
string

Org ID (UUID)

src_id
required
string

State Source ID (UUID)

Responses

Request samples

curl --request POST \
  --url https://app.driftwise.ai/api/v2/orgs/%7Bid%7D/state-sources/%7Bsrc_id%7D/refresh \
  --header 'X-API-Key: REPLACE_KEY_VALUE' \
  --header 'accept: application/json'

Response samples

Content type
application/json
{
  • "content_hash": "string",
  • "last_fetched_at": "string",
  • "refreshed": true,
  • "resource_count": 0,
  • "snapshot_id": "string"
}