{
  "openapi": "3.1.0",
  "info": {
    "title": "Factify API",
    "version": "1.1.1",
    "description": "Factify API enables organizations to ingest records, manage workflows, and extract auditable facts.\n\n## Authentication\n\nBearer authentication header of the form `Bearer <token>`, where `<token>` is your auth token.\n\n```\nAuthorization: Bearer ffy_prod_<base32_uuid><base62_random>\n```\n\n## Rate Limiting\n\nPer-API-key rate limits apply. Concrete thresholds are configured per\ndeployment and surface via standard `Retry-After` and `RateLimit-*`\nresponse headers when a request is throttled.\n\n## Errors\n\nFactify uses conventional HTTP status codes and returns structured error responses following [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (Problem Details for HTTP APIs).",
    "contact": {
      "name": "Factify API Support",
      "email": "api@factify.com",
      "url": "https://factify.com/support"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://factify.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.factify.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Organizations",
      "description": "Organizations and their settings."
    },
    {
      "name": "Members",
      "description": "Membership roster and roles within an organization."
    },
    {
      "name": "Invitations",
      "description": "Pending invitations to join an organization."
    },
    {
      "name": "Users",
      "description": "The authenticated user's profile."
    },
    {
      "name": "ApiKeys",
      "description": "API keys used to authenticate SDK and integration callers."
    },
    {
      "name": "Usage",
      "description": "Per-organization usage and quota limits. Future operations covering\nAPI-key-level quotas and usage history land here too.\n"
    },
    {
      "name": "Connections",
      "description": "Inbound integration connections that records are ingested from."
    },
    {
      "name": "Workflows",
      "description": "Workflow templates that define the rubric set for fact extraction."
    },
    {
      "name": "Plans",
      "description": "Workflow instances — bind claims and lock to produce an extraction spec."
    },
    {
      "name": "Records",
      "description": "Generic JSON records ingested for claim extraction."
    },
    {
      "name": "Audit",
      "description": "Unified audit and provenance events."
    },
    {
      "name": "Notifications",
      "description": "In-app notification feed backed by audit events and per-user read state."
    },
    {
      "name": "Internal",
      "description": "Internal callbacks not intended for public SDK consumers."
    },
    {
      "name": "RubricContentions",
      "description": "Rubric contention detection and resolution — surface and resolve competing claim bindings."
    },
    {
      "name": "IdentityProviders",
      "description": "Per-organization SAML identity providers for SSO."
    },
    {
      "name": "Analytics",
      "description": "Org-scoped execution analytics — volume, decision breakdown, and latency over plan executions."
    },
    {
      "name": "Auth",
      "description": "Browser-facing authentication surface — CSRF, session, OAuth callback,\nOTP, passkey, and refresh-token endpoints. These predate factapi's\nversioned `/v1/*` SDK surface and use the BetterAuth wire format the\nworkspace SPA already speaks; SDK generators skip them via\n`x-speakeasy-ignore`.\n"
    }
  ],
  "paths": {
    "/v1/me": {
      "get": {
        "operationId": "getUser",
        "summary": "Get current user",
        "description": "Returns the authenticated user's profile.\n\nOnly available to user accounts (Cognito session, OAuth, or\ndev JWT). API-key callers have no user identity and receive\n403.\n",
        "tags": [
          "Users"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "users",
        "x-speakeasy-name-override": "getMe"
      },
      "patch": {
        "operationId": "updateUser",
        "summary": "Update current user profile",
        "description": "Updates the authenticated user's profile. Uses RFC 7396\n(JSON Merge Patch) semantics: omitted fields are left\nunchanged. `display_name` is required on every request and\ncannot be cleared.\n\n`given_name` and `family_name` accept `null` to clear. Empty\nstring is rejected with 422 — use `null`.\n\nEmail cannot be updated through this endpoint.\n",
        "tags": [
          "Users"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateMeInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "users",
        "x-speakeasy-name-override": "updateMe"
      }
    },
    "/v1/users": {
      "get": {
        "operationId": "listUsers",
        "summary": "List users (platform admin)",
        "description": "Lists users across the deployment, newest first. Restricted to\nFactify staff (caller must be signed in with an `@factify.com`\nemail). API keys and non-staff callers receive 403.\n\nPagination follows the standard factapi pattern: pass `page_size`\nfor the page bound; on the next call, set `page_token` to the\nopaque token returned in the `next` URL. Without `page_token`,\nthe first page is returned.\n\nThe optional `organization_id` filter restricts the listing to\nusers with membership in that org. Combine with `page_size` to\npage through one organization's users in isolation.\n",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "page_size",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of users to return. Defaults to 20; capped at 100.\n"
          },
          {
            "in": "query",
            "name": "page_token",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor returned in the previous response's `next` URL.\nOmit on the first request.\n"
          },
          {
            "in": "query",
            "name": "organization_id",
            "schema": {
              "type": "string",
              "pattern": "^org_[0-9a-z]{26}$"
            },
            "description": "Restrict to members of this organization. TypedID; the\n`org_` prefix is required.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserListOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "users",
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createUser",
        "summary": "Create user (platform admin)",
        "description": "Provisions a new user row. Restricted to Factify staff (caller\nmust be signed in with an `@factify.com` email). API keys and\nnon-staff callers receive 403.\n\nUsed by the Factify backoffice to seed users that bypass the\nnormal Cognito sign-in flow (e.g. test accounts, support\nimpersonation prep). The `auth_sub` is admin-supplied and links\nthe user row to the auth provider's subject claim.\n\nWhen `organization_id` is provided, the new user is added as a\nmember of that org with role `member` in the same transaction.\n\nReturns 409 if a user with the same email or `auth_sub` already\nexists, and 404 if `organization_id` refers to an unknown org.\n",
        "tags": [
          "Users"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserInputBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "users",
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/users/{user_id}": {
      "get": {
        "operationId": "getUserById",
        "summary": "Get user by id (platform admin)",
        "description": "Returns a single user by TypedID. Restricted to Factify staff\n(caller must be signed in with an `@factify.com` email).\nAPI keys and non-staff callers receive 403.\n\nReturns 404 when no user with that id exists. Returns the same\n404 for a syntactically-invalid id so unknown-but-invalid ids\nblend with unknown-but-valid ids — the caller's input alone\ndoes not leak whether the id was malformed.\n",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^usr_[0-9a-z]{26}$"
            },
            "description": "TypedID of the user."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "users",
        "x-speakeasy-name-override": "get"
      }
    },
    "/v1/organizations": {
      "get": {
        "operationId": "listOrganizations",
        "summary": "List organizations",
        "description": "Lists organizations the authenticated user belongs to (as\nowner, admin, or member).\n\nRequires a user-bearing auth method (session, JWT, or OAuth);\nAPI-key / service-account callers receive 403.\n",
        "tags": [
          "Organizations"
        ],
        "responses": {
          "200": {
            "description": "A page of organizations the caller belongs to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationListOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "organizations",
        "x-speakeasy-name-override": "list"
      }
    },
    "/v1/organization": {
      "get": {
        "operationId": "getOrganization",
        "summary": "Get the current organization",
        "description": "Returns the organization the caller's token is scoped to —\nthe owning organization for API-key callers, the selected\norganization for user-session callers.\n",
        "tags": [
          "Organizations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The caller's current organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "organizations",
        "x-speakeasy-name-override": "get"
      },
      "post": {
        "operationId": "createOrganization",
        "summary": "Create an organization",
        "description": "Creates a new organization with the authenticated user as the\nfirst owner.\n\nService-account callers cannot create organizations; the call\nreturns 403.\n",
        "tags": [
          "Organizations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The newly created organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "organizations",
        "x-speakeasy-name-override": "create"
      },
      "patch": {
        "operationId": "updateOrganization",
        "summary": "Update the organization",
        "description": "Updates the caller's current organization. Only the name is\nmutable via this endpoint.\n\nAuthorization: requires `organization#update` (owner only).\n",
        "tags": [
          "Organizations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrganizationInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "organizations",
        "x-speakeasy-name-override": "update"
      }
    },
    "/v1/quota": {
      "get": {
        "operationId": "getQuota",
        "summary": "Get document quota and usage",
        "description": "Returns the document quota for the caller's organisation:\nthe limit, the current document count, the remaining\nbalance, and the next reset timestamp.\n",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "Current quota and usage for the caller's organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "get"
      }
    },
    "/v1/me/quota": {
      "get": {
        "operationId": "getMyQuota",
        "summary": "Get usage and quota for the caller's active organisation",
        "description": "Returns current usage and quota for the authenticated user's\ncurrently-active organisation. Requires a user-bearing auth\nmethod (session/JWT/OAuth); API-key callers — which carry no\nuser identity — receive 403.\n",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "getMine"
      }
    },
    "/v1/workflow-quota": {
      "get": {
        "operationId": "getWorkflowQuota",
        "summary": "Get workflow policy quota and usage",
        "description": "Returns the workflow/policy quota for the caller's organisation:\nwhether enforcement is enabled, the limit, the current workflow\ncount, and the remaining balance.\n",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "Current workflow policy quota and usage for the caller's organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowQuotaOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "getWorkflow"
      }
    },
    "/v1/me/workflow-quota": {
      "get": {
        "operationId": "getMyWorkflowQuota",
        "summary": "Get workflow policy quota for the caller's active organisation",
        "description": "Returns workflow/policy quota for the authenticated user's\ncurrently-active organisation. Requires a user-bearing auth\nmethod (session/JWT/OAuth); API-key callers receive 403.\n",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowQuotaOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "getMyWorkflow"
      }
    },
    "/v1/organizations/{organization_id}/quota": {
      "get": {
        "operationId": "getOrganizationQuota",
        "summary": "Get organization quota and usage (platform admin)",
        "description": "Returns the current quota status for an organization: configured\ntier, effective monthly limit, current usage, remaining balance,\nand the next reset boundary. Restricted to Factify platform\nstaff.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the organization.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationQuotaOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "getOrganizationQuota"
      }
    },
    "/v1/organizations/{organization_id}/usage": {
      "get": {
        "operationId": "getUsageHistory",
        "summary": "Get daily usage history for an organization (platform admin)",
        "description": "Returns per-day usage rows for the organization. Optional\n`date_after` filter narrows the result to dates on or after\nthe supplied calendar date (YYYY-MM-DD).\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the organization.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
            }
          },
          {
            "name": "date_after",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `usage_date`. Accepts RFC3339\ntimestamps or `YYYY-MM-DD`; only the calendar date is used\nfor the comparison.\n",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-05-01T00:00:00Z"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetUsageHistoryOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "getUsageHistory"
      }
    },
    "/v1/organizations/{organization_id}/api-key-quotas": {
      "get": {
        "operationId": "listAPIKeyQuotas",
        "summary": "List per-API-key quota overrides for an organization (platform admin)",
        "description": "Returns the per-API-key quota overrides configured for the\norganization. Keys without overrides are omitted from the\nresponse — they're metered against the org-level limit only.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the organization.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListAPIKeyQuotasOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "listAPIKeyQuotas"
      }
    },
    "/v1/api-keys/{api_key_id}/quota": {
      "put": {
        "operationId": "setAPIKeyQuota",
        "summary": "Create or update a per-API-key quota override (platform admin)",
        "description": "Sets a monthly request limit on a single API key. The key is\nthen metered independently of (and capped by) the org-level\nlimit. Subsequent calls overwrite the previous limit.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "api_key_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the API key.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetAPIKeyQuotaInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetAPIKeyQuotaOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "setAPIKeyQuota"
      },
      "delete": {
        "operationId": "deleteAPIKeyQuota",
        "summary": "Remove a per-API-key quota override (platform admin)",
        "description": "Deletes the per-key quota override. The key reverts to being\nmetered against the org-level limit only. Returns 204 on\nsuccess, 404 when the override does not exist.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "api_key_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the API key.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
            }
          },
          {
            "name": "organization_id",
            "in": "query",
            "required": false,
            "description": "Optional org scope guard. When set, the server verifies the\nkey belongs to that org before deleting.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "usage",
        "x-speakeasy-name-override": "deleteAPIKeyQuota"
      }
    },
    "/v1/organization/invitations": {
      "get": {
        "operationId": "listInvitations",
        "summary": "List pending invitations",
        "description": "Lists pending invitations for the caller's current\norganization.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Invitations"
        ],
        "x-speakeasy-group": "organizations.invites",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of pending invitations for the organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationListOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createInvitation",
        "summary": "Invite a member by email",
        "description": "Creates an invitation for the given email and role.\nInvitations expire 7 days after creation.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Invitations"
        ],
        "x-speakeasy-group": "organizations.invites",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvitationInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created (or refreshed) invitation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrgInvitation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/organization/invitations/{invitation_id}": {
      "delete": {
        "operationId": "revokeInvitation",
        "summary": "Revoke an invitation",
        "description": "Revokes a pending invitation. Any link previously sent to the\nrecipient becomes invalid.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Invitations"
        ],
        "x-speakeasy-group": "organizations.invites",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "invitation_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the invitation to revoke.\nPattern: `inv_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "inv_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "revoke"
      }
    },
    "/v1/organization/invitations/{invitation_id}/accept": {
      "post": {
        "operationId": "acceptInvitation",
        "summary": "Accept an invitation",
        "description": "Accepts a pending invitation and adds the authenticated user\nas a member of the organization with the invited role.\n\nReturns 404 if the invitation does not exist and 422 if it\nhas expired.\n",
        "tags": [
          "Invitations"
        ],
        "x-speakeasy-group": "organizations.invites",
        "parameters": [
          {
            "name": "invitation_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the invitation to accept.\nPattern: `inv_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "inv_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "accept"
      }
    },
    "/v1/organization/invitations/{invitation_id}/resend": {
      "post": {
        "operationId": "resendInvitation",
        "summary": "Resend an invitation email",
        "description": "Resends the invitation email to the recipient. Useful when the\noriginal email was lost or close to expiring. Only pending,\nnon-expired invitations can be resent.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Invitations"
        ],
        "x-speakeasy-group": "organizations.invites",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "invitation_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the invitation to resend.\nPattern: `inv_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "inv_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "resend"
      }
    },
    "/v1/organization/members": {
      "get": {
        "operationId": "listMembers",
        "summary": "List organization members",
        "description": "Lists members of the caller's current organization.\n\nAuthorization: requires `organization#list_members`, which the\nSpiceDB schema grants to any organization member (owner, admin,\nor regular member).\n",
        "tags": [
          "Members"
        ],
        "x-speakeasy-group": "organizations.members",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of organization members.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberListOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "list"
      }
    },
    "/v1/organization/members/{user_id}": {
      "delete": {
        "operationId": "removeMember",
        "summary": "Remove a member",
        "description": "Removes a member from the organization. Returns 422 if the\nuser is the last remaining owner.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Members"
        ],
        "x-speakeasy-group": "organizations.members",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the user to remove.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "remove"
      },
      "patch": {
        "operationId": "updateMemberRole",
        "summary": "Update a member's role",
        "description": "Updates a member's role within the organization. Returns 422\nif the change would demote the last remaining owner.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "Members"
        ],
        "x-speakeasy-group": "organizations.members",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the user whose role is being updated.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateMemberRoleInputBody"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "update"
      }
    },
    "/v1/api-keys": {
      "get": {
        "operationId": "listApiKeys",
        "summary": "List API keys",
        "description": "Lists API keys for the caller's organization. Secrets are\nnever returned — use the `prefix` field to identify a key in\nthe UI.\n\nAuthorization: requires organization admin role.\n",
        "tags": [
          "ApiKeys"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "include_revoked",
            "in": "query",
            "description": "Whether to include revoked keys. Defaults to `false`.\nExpired-but-not-revoked keys are always returned; use the\n`is_active` field to filter currently usable keys.\n",
            "required": false,
            "schema": {
              "type": "boolean",
              "example": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of API keys for the organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListAPIKeysOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "apiKeys",
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createApiKey",
        "summary": "Create an API key",
        "description": "Creates a new API key for the caller's organization. The full\nsecret is returned exactly once in the response and cannot be\nretrieved later — store it securely on receipt.\n\nKeys cannot be updated; rotate by creating a new key and\nrevoking the old one.\n\nAuthorization: requires organization admin role.\n",
        "tags": [
          "ApiKeys"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAPIKeyInputBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The newly created API key, including the full secret (returned exactly once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIKeyCreatedOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "apiKeys",
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/api-keys/{api_key_id}": {
      "delete": {
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key",
        "description": "Revokes an API key, immediately preventing it from being used\nfor authentication. Revocation is permanent — to restore\naccess, create a new key.\n\nAuthorization: requires organization admin role.\n",
        "tags": [
          "ApiKeys"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "api_key_id",
            "in": "path",
            "description": "TypedID of the API key to revoke.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "required": true,
            "schema": {
              "type": "string",
              "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeAPIKeyBody"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "apiKeys",
        "x-speakeasy-name-override": "revoke"
      }
    },
    "/v1/connections": {
      "get": {
        "operationId": "listConnections",
        "summary": "List connections",
        "description": "Returns the inbound integration connections of the caller's\norganization, most-recently-created first.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListConnectionsOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createConnection",
        "summary": "Create a connection",
        "description": "Creates a pending connection for a provider account and opens a\nshort-lived connect session. Send the account owner to the\nsession's `connect_link` to authorise access, then poll\n`GET /v1/connections/{connection_id}/status` until the\nconnection leaves `pending`. Requires a caller with a user\nidentity — records ingested through the connection are\nattributed to it.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateConnectionInputBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateConnectionOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/connections/{connection_id}": {
      "get": {
        "operationId": "getConnection",
        "summary": "Get a connection",
        "description": "Returns a connection by ID. This is a stored read; use\n`GET /v1/connections/{connection_id}/status` to reconcile\nagainst the live transport state. A non-existent or\ncross-organization `connection_id` returns 404.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "get"
      },
      "patch": {
        "operationId": "updateConnection",
        "summary": "Rename a connection",
        "description": "Updates a connection's `display_name`. A non-existent or\ncross-organization `connection_id` returns 404; a blank name\nreturns 422.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateConnectionInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "update"
      },
      "delete": {
        "operationId": "deleteConnection",
        "summary": "Disconnect a connection",
        "description": "Revokes the connection's credential grant at the transport\n(best-effort) and soft-deletes the connection. Ingestion from\nthe account stops. A non-existent or cross-organization\n`connection_id` returns 404.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "delete"
      }
    },
    "/v1/connections/{connection_id}/status": {
      "get": {
        "operationId": "getConnectionStatus",
        "summary": "Check live connection status",
        "description": "Reconciles the connection against the live transport state and\nreturns the updated connection. For a `pending` connection this\ndiscovers whether the OAuth flow completed (activating the\nconnection and filling `external_account_id`); for an active\none it detects revocation or credential errors. Poll this after\nopening `connect_link`.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "getStatus"
      }
    },
    "/v1/connections/{connection_id}/connect-session": {
      "post": {
        "operationId": "createConnectSession",
        "summary": "Re-issue a connect session",
        "description": "Opens a fresh connect session for an existing connection: a new\n`connect_link` for a `pending` connection whose previous session\nexpired, or a re-authorisation flow for an `active`/`error`\nconnection whose credentials need repair. A non-existent or\ncross-organization `connection_id` returns 404.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectSessionOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "createConnectSession"
      }
    },
    "/v1/connections/{connection_id}/sources": {
      "get": {
        "operationId": "listSources",
        "summary": "List sources on a connection",
        "description": "Returns the connection's configured ingest sources, oldest\nfirst. A non-existent or cross-organization `connection_id`\nreturns 404.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListSourcesOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections.sources",
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createSource",
        "summary": "Create a source on a connection",
        "description": "Configures an ingest source on an `active` connection: for\nGmail, a watched label (or the whole mailbox when `label_id`\nis omitted). Polling starts within about a minute. The\nconnection must be `active` (409 otherwise); watching the\nsame label twice returns 409. Requires a caller with a user\nidentity (422 otherwise). Sources are currently Gmail-only —\nother providers return 422.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSourceInputBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundSource"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections.sources",
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/connections/{connection_id}/sources/{source_id}": {
      "get": {
        "operationId": "getSource",
        "summary": "Get a source",
        "description": "Returns a source by ID. A non-existent or cross-organization\n`connection_id`/`source_id` returns 404.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          },
          {
            "$ref": "#/components/parameters/SourceID"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundSource"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections.sources",
        "x-speakeasy-name-override": "get"
      },
      "patch": {
        "operationId": "updateSource",
        "summary": "Enable or disable a source",
        "description": "Updates a source's `enabled` flag. Disabling stops polling\nwithin about a minute; already-ingested records are kept. Enabling a\ncompany ontology source atomically makes it authoritative and disables\nthe previous authority. An authoritative ontology source cannot be\ndisabled without enabling its replacement.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          },
          {
            "$ref": "#/components/parameters/SourceID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSourceInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundSource"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections.sources",
        "x-speakeasy-name-override": "update"
      },
      "delete": {
        "operationId": "deleteSource",
        "summary": "Delete a source",
        "description": "Deletes a source. Polling stops within about a minute;\nalready-ingested records are kept.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          },
          {
            "$ref": "#/components/parameters/SourceID"
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections.sources",
        "x-speakeasy-name-override": "delete"
      }
    },
    "/v1/connections/{connection_id}/available-labels": {
      "get": {
        "operationId": "listAvailableLabels",
        "summary": "List labels available on a connection",
        "description": "Enumerates the labels on the connected account (for Gmail, the\nmailbox's labels) to pick a `label_id` for source creation.\nThe connection must be `active` (409 otherwise); a provider\nwithout label semantics returns 422.\n",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "$ref": "#/components/parameters/ConnectionID"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListLabelsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "connections",
        "x-speakeasy-name-override": "listAvailableLabels"
      }
    },
    "/v1/company-ontology": {
      "get": {
        "operationId": "getCompanyOntology",
        "summary": "Get the current company ontology",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Current authoritative company ontology state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyOntologyOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "companyOntology",
        "x-speakeasy-name-override": "get"
      }
    },
    "/v1/company-ontology/imports/preview": {
      "post": {
        "operationId": "previewCompanyOntologyImport",
        "summary": "Validate and preview a manual company ontology CSV import",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PreviewCompanyOntologyImportBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import preview.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewCompanyOntologyImportOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "companyOntology.imports",
        "x-speakeasy-name-override": "preview"
      }
    },
    "/v1/company-ontology/imports": {
      "post": {
        "operationId": "commitCompanyOntologyImport",
        "summary": "Commit a previewed manual company ontology CSV import",
        "tags": [
          "Connections"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CommitCompanyOntologyImportBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import committed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitCompanyOntologyImportOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "companyOntology.imports",
        "x-speakeasy-name-override": "commit"
      }
    },
    "/v1/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "summary": "List workflows",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "list",
        "responses": {
          "200": {
            "description": "Workflows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListWorkflowsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "createWorkflow",
        "summary": "Create workflow",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "create",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWorkflowBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Workflow created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}": {
      "get": {
        "operationId": "getWorkflow",
        "summary": "Get workflow",
        "description": "Returns the active revision if one exists, otherwise the draft.",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "get",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Workflow found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "updateWorkflow",
        "summary": "Update workflow",
        "description": "Renames the workflow. Only the name field can be changed here; use PUT /draft for body_md and slots.",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "update",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWorkflowBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workflow updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteWorkflow",
        "summary": "Trash workflow",
        "description": "Soft-deletes the workflow so it no longer appears in active workflow lists. This is exposed in the app as \"Trash process\"; restore and include-trashed listing are not available yet.",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "delete",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "204": {
            "description": "Workflow trashed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/draft": {
      "get": {
        "operationId": "getWorkflowDraft",
        "summary": "Get workflow draft",
        "description": "Returns the draft revision's content (body_md and rubric slots).",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "getDraft",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDraft"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "editWorkflowDraft",
        "summary": "Edit workflow draft",
        "description": "Updates the draft revision's body_md and rubric slots. The slots array\nrepresents the complete desired state (diff is computed server-side).\n",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "editDraft",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditDraftBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/draft/activate": {
      "post": {
        "operationId": "activateWorkflowDraft",
        "summary": "Activate workflow draft",
        "description": "Promotes the draft revision to active. Demotes the prior active revision\nto inactive and seeds a new draft as a full clone of the just-activated\nrevision. Returns the updated workflow (showing the newly active content).\n",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "activateDraft",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft activated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/synth": {
      "post": {
        "operationId": "createWorkflowSynth",
        "summary": "Create the L4 synth row in 'pending'",
        "description": "Idempotent insert. Called as the first activity of the taco-worker-side\nSynthWorkflow so the L3 CreateWorkflow tx stays pure — the L4 row\nis owned by L4 end-to-end. Returns the row in 'pending' state.\n",
        "tags": [
          "Synth"
        ],
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Synth row created (or already exists).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowSynth"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "get": {
        "operationId": "getWorkflowSynth",
        "summary": "Read the workflow's synth state",
        "description": "Returns the L4 synth row: state, artifact_uri (when ready), error\n(when failed), updated_at. Org-scoped via the parent workflow's\nownership — non-owners get 404, never the row's contents.\n",
        "tags": [
          "Synth"
        ],
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Current synth state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowSynth"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateWorkflowSynth",
        "summary": "Advance the workflow's synth state",
        "description": "State machine writer. Used by the taco-worker-side SynthWorkflow to\ntransition pending → compiling → ready|failed as the LLM call,\nbuild gate, and S3 upload progress. Service-layer enforces the\nlegal transitions; illegal moves return 422.\n\nRequired body fields by target state:\n  - state=ready:  artifact_uri MUST be set\n  - state=failed: error MUST be set\n  - state=compiling: no other fields needed\n",
        "tags": [
          "Synth"
        ],
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWorkflowSynthBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated synth state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowSynth"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/workflow-issues": {
      "get": {
        "operationId": "listWorkflowIssues",
        "summary": "List workflow issues",
        "description": "Returns open workflow issues and the current detection status for the given workflow.",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "listIssues",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Workflow issues and detection status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowIssuesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/workflow-issues/{issueId}": {
      "patch": {
        "operationId": "dismissWorkflowIssue",
        "summary": "Dismiss a workflow issue",
        "description": "Sets the status of a workflow issue to 'dismissed'. Dismissed issues are excluded from the list endpoint. The dismissed state survives re-detection.",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "dismissIssue",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          },
          {
            "name": "issueId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow issue TypeID (wisu_…)"
          }
        ],
        "responses": {
          "204": {
            "description": "Issue dismissed successfully."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/workflows/{workflowId}/sources": {
      "get": {
        "operationId": "listWorkflowSources",
        "summary": "List workflow sources",
        "description": "Returns the upstream sources a workflow was authored from (its provenance — \"which sources fed this policy?\").",
        "tags": [
          "Workflows"
        ],
        "x-speakeasy-group": "workflows",
        "x-speakeasy-name-override": "listSources",
        "parameters": [
          {
            "name": "workflowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workflow TypeID (wfl_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "The sources the workflow was authored from.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowSourcesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans": {
      "get": {
        "operationId": "listPlans",
        "summary": "List plans",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "list",
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 1,
              "default": 1
            },
            "description": "1-indexed page number."
          },
          {
            "in": "query",
            "name": "page_size",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of plans per page."
          }
        ],
        "responses": {
          "200": {
            "description": "Plans.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPlansResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "openPlan",
        "summary": "Open plan",
        "description": "Instantiate a new plan from a workflow.",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "open",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlanBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Plan opened.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}": {
      "get": {
        "operationId": "getPlan",
        "summary": "Get plan",
        "description": "Returns the plan with inline claim_slots and submissions. body_md_resolved is populated only after the plan is locked.",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "get",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Plan found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "patch": {
        "operationId": "updatePlan",
        "summary": "Update plan",
        "description": "Renames the plan. Only the name field can be changed here.",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "update",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePlanBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Plan updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}/lock": {
      "post": {
        "operationId": "lockPlan",
        "summary": "Lock plan",
        "description": "Locks the plan, computing and storing body_md_resolved from bound claims.\nPure-render path — does NOT invoke any workflow body. Execution lives\nseparately at POST /v1/plans/{planId}/run (Runtime layer) and\nrequires a prior /lock; the plan + bound claims are immutable after lock.\n\nReturns 409 when the plan cannot be locked. The `floating_rubrics` field\nis present only when the conflict is caused by plan-scoped rubric slots\nthat CLW Pass A matching has not yet bound; other conflict reasons (already locked,\nsubmission still processing) omit it.\n",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "lock",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Plan locked successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Plan cannot be locked. When caused by floating plan-scoped rubric\nslots, `floating_rubrics` lists the unbound slots.\n",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/LockPlanConflictBody"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}/facts/{rubricId}/binding": {
      "post": {
        "operationId": "bindPlanFact",
        "summary": "Bind a plan-scoped Fact rubric",
        "description": "Records an operator-declared value for a plan-scoped rubric: persists\nthe declaration as a doc-free, record-backed claim at explicit-approval\nconfidence and upserts the plan-level binding, unconditionally\nreplacing any prior value. Returns 422 if `rubricId` names a\nworkflow-scoped rubric — binding via this endpoint is plan-scope only.\n",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "bindFact",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          },
          {
            "name": "rubricId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Rubric TypeID (rub_…)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BindPlanFactBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rubric bound. Returns the updated plan fact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanFact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The plan is locked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}/rubrics/{rubricId}/history": {
      "get": {
        "operationId": "getPlanRubricHistory",
        "summary": "Get a plan rubric's value history",
        "description": "Returns every committed value transition for a rubric in this plan,\nnewest first. A plan or rubric outside the caller's organization is\nreturned as not found.\n",
        "tags": [
          "Plans"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "getRubricHistory",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          },
          {
            "name": "rubricId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Rubric TypeID (rub_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Rubric value history, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanRubricHistoryResponse"
                },
                "example": {
                  "entries": [
                    {
                      "id": "bind_01k1bkq1yxf0k8mnb7t0ab1cde",
                      "changed_at": "2026-07-28T10:00:00Z",
                      "previous": {
                        "claim_id": "clm_01k1bkpg30fcq8mnb7t0ab1cde",
                        "value": "165000"
                      },
                      "current": {
                        "claim_id": "clm_01k1bkq1yxf0k8mnb7t0ab1cdf",
                        "value": "175000"
                      },
                      "actor": {
                        "type": "user",
                        "id": "usr_01k1bkmb7jf0k8mnb7t0ab1cde",
                        "label": "Alex Morgan"
                      },
                      "source": {
                        "record_id": "rec_01k1bknmcaf0k8mnb7t0ab1cde",
                        "filename": "declarations.pdf",
                        "mime_type": "application/pdf",
                        "block_path": "page 4",
                        "statement": "Coverage A is $175,000"
                      }
                    },
                    {
                      "id": "bind_01k1bkpg30fcq8mnb7t0ab1cdf",
                      "changed_at": "2026-07-28T09:30:00Z",
                      "previous": {
                        "claim_id": "clm_01k1bknmcaf0k8mnb7t0ab1cdd",
                        "value": "150000"
                      },
                      "current": {
                        "claim_id": "clm_01k1bkpg30fcq8mnb7t0ab1cde",
                        "value": "165000"
                      },
                      "actor": {
                        "type": "user",
                        "id": "usr_01k1bkmb7jf0k8mnb7t0ab1cdf",
                        "label": "Jordan Lee"
                      },
                      "source": null
                    },
                    {
                      "id": "bind_01k1bknmcaf0k8mnb7t0ab1cdf",
                      "changed_at": "2026-07-28T08:15:00Z",
                      "previous": null,
                      "current": {
                        "claim_id": "clm_01k1bknmcaf0k8mnb7t0ab1cdd",
                        "value": "150000"
                      },
                      "actor": {
                        "type": "unknown",
                        "id": null,
                        "label": "Actor not recorded"
                      },
                      "source": {
                        "record_id": "rec_01k1bknmcaf0k8mnb7t0ab1cdd",
                        "filename": "application.pdf",
                        "mime_type": "application/pdf",
                        "block_path": "page 2",
                        "statement": "Coverage A is $150,000"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}/run": {
      "post": {
        "operationId": "runPlan",
        "summary": "Run locked plan",
        "description": "Dispatches the plan's workflow body via Temporal, captures the decision\nrecord, and appends a new row to taco.plan_executions. Pre-conditions:\n  * Plan exists and belongs to the caller's org.\n  * Plan is locked (locked_at != nil) — call POST /v1/plans/{planId}/lock first.\nRe-running is allowed; each run appends a new row. Plan + claim\nbindings remain immutable. The MCP `tools/call` JSON-RPC method\nproxies through this endpoint.\n",
        "tags": [
          "Runtime"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "run",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          }
        ],
        "responses": {
          "200": {
            "description": "Workflow run; new run row appended. Response carries the persisted decision record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/plans/{planId}/runs": {
      "get": {
        "operationId": "listPlanRuns",
        "summary": "List runs for a plan",
        "description": "Returns the run history of a plan, newest finished_at first.\nScoped to the caller's org via the parent plan's ownership.\n",
        "tags": [
          "Runtime"
        ],
        "x-speakeasy-group": "plans",
        "x-speakeasy-name-override": "listRuns",
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Plan TypeID (pln_…)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-indexed page number."
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size (1..100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated run list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPlanRunsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/rubric-contentions": {
      "get": {
        "operationId": "listRubricContentions",
        "summary": "List open rubric contentions",
        "description": "Returns all open rubric contentions visible to the caller's org. Optionally\nfiltered by planId or workflowId.\n",
        "tags": [
          "Rubric Contentions"
        ],
        "x-speakeasy-group": "rubricContentions",
        "x-speakeasy-name-override": "list",
        "parameters": [
          {
            "name": "planId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to rubric contentions on a specific plan (TypeID, e.g. pln_…)."
          },
          {
            "name": "workflowId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to rubric contentions on a specific workflow (TypeID, e.g. wfl_…)."
          }
        ],
        "responses": {
          "200": {
            "description": "Open rubric contentions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListRubricContentionsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/rubric-contentions/{contentionId}": {
      "get": {
        "operationId": "getRubricContention",
        "summary": "Get rubric contention detail",
        "description": "Returns a rubric contention with its candidates and incumbent claim info.",
        "tags": [
          "Rubric Contentions"
        ],
        "x-speakeasy-group": "rubricContentions",
        "x-speakeasy-name-override": "get",
        "parameters": [
          {
            "name": "contentionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Rubric contention TypeID (e.g. ctnd_…)."
          }
        ],
        "responses": {
          "200": {
            "description": "Rubric contention found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RubricContentionDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/rubric-contentions/{contentionId}/resolve": {
      "post": {
        "operationId": "resolveRubricContention",
        "summary": "Resolve a rubric contention",
        "description": "Resolves an open rubric contention in a single CAS transaction. Exactly one of\n`winner_candidate_id` or `reject_all` must be set.\n\n**Pick-winner**: promotes the candidate's claim as the new binding for the\nslot; the incumbent is superseded. Fails (409) if the contention is already\nresolved, the plan is locked, or the slot's current binding has changed since\nthe contention was detected (stale CAS).\n\n**Reject-all** (`reject_all: true`): closes the contention without changing\nthe binding; the incumbent claim remains current.\n",
        "tags": [
          "Rubric Contentions"
        ],
        "x-speakeasy-group": "rubricContentions",
        "x-speakeasy-name-override": "resolve",
        "parameters": [
          {
            "name": "contentionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Rubric contention TypeID (e.g. ctnd_…)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRubricContentionBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rubric contention resolved. Returns the updated rubric contention detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RubricContentionDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/analytics/runs": {
      "get": {
        "operationId": "getRunAnalytics",
        "summary": "Run analytics for the caller's organization",
        "description": "Returns aggregated run analytics for the authenticated caller's\norganization over the `[from, to)` window: a headline summary (total,\napprove count, approval rate, average latency, p50 latency, and p95\nlatency), daily run volume, a decision breakdown, and the top\nworkflows by run volume. When `from`/`to` are omitted the server\ndefaults to the last 30 days.\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `started_at` (RFC3339). Defaults to 30\ndays before `to` when omitted.\n",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-05-17T00:00:00Z"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `started_at` (RFC3339). Defaults to now\nwhen omitted.\n",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-06-16T00:00:00Z"
            }
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow TypeID (wfl_…) to scope the aggregate to. Omit for the org-wide aggregate across all workflows.",
            "schema": {
              "type": "string",
              "example": "wfl_01kx2t1nkffvha1ky4zbnpff8x"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunAnalyticsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getRunAnalytics"
      }
    },
    "/v1/analytics/runs/recent": {
      "get": {
        "operationId": "listRecentRuns",
        "summary": "Paginated recent runs for the caller's organization",
        "description": "Returns one page of the caller's organization's runs over the\n`[from, to)` window, newest first, for the analytics drill-down table.\n`from`/`to` default to the last 30 days as with the aggregate endpoint.\n`page` is 1-based; `pageSize` is clamped to [1, 100] (default 25).\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `started_at` (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-05-17T00:00:00Z"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `started_at` (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-06-16T00:00:00Z"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page index. Defaults to 1.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Rows per page, clamped to [1, 100]. Defaults to 25.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "example": 25
            }
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow TypeID (wfl_…) to scope the aggregate to. Omit for the org-wide aggregate across all workflows.",
            "schema": {
              "type": "string",
              "example": "wfl_01kx2t1nkffvha1ky4zbnpff8x"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecentRunsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "listRecentRuns"
      }
    },
    "/v1/analytics/executions": {
      "get": {
        "operationId": "getExecutionAnalytics",
        "summary": "Execution analytics for the caller's organization",
        "deprecated": true,
        "description": "Deprecated: use `getRunAnalytics` (`GET /v1/analytics/runs`) instead, same\nresponse shape. Returns aggregated execution analytics for the\nauthenticated caller's organization over the `[from, to)` window: a\nheadline summary (total, approve count, approval rate, average latency,\np50 latency, and p95 latency), daily execution volume, a decision\nbreakdown, and the top workflows by execution volume. When `from`/`to`\nare omitted the server defaults to the last 30 days.\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `started_at` (RFC3339). Defaults to 30\ndays before `to` when omitted.\n",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-05-17T00:00:00Z"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `started_at` (RFC3339). Defaults to now\nwhen omitted.\n",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-06-16T00:00:00Z"
            }
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow TypeID (wfl_…) to scope the aggregate to. Omit for the org-wide aggregate across all workflows.",
            "schema": {
              "type": "string",
              "example": "wfl_01kx2t1nkffvha1ky4zbnpff8x"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecutionAnalyticsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getExecutionAnalytics"
      }
    },
    "/v1/analytics/executions/recent": {
      "get": {
        "operationId": "listRecentExecutions",
        "summary": "Paginated recent executions for the caller's organization",
        "deprecated": true,
        "description": "Deprecated: use `listRecentRuns` (`GET /v1/analytics/runs/recent`)\ninstead, same response shape. Returns one page of the caller's\norganization's executions over the `[from, to)` window, newest first,\nfor the analytics drill-down table.\n`from`/`to` default to the last 30 days as with the aggregate endpoint.\n`page` is 1-based; `pageSize` is clamped to [1, 100] (default 25).\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `started_at` (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-05-17T00:00:00Z"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `started_at` (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-06-16T00:00:00Z"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page index. Defaults to 1.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Rows per page, clamped to [1, 100]. Defaults to 25.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "example": 25
            }
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow TypeID (wfl_…) to scope the aggregate to. Omit for the org-wide aggregate across all workflows.",
            "schema": {
              "type": "string",
              "example": "wfl_01kx2t1nkffvha1ky4zbnpff8x"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecentExecutionsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "listRecentExecutions"
      }
    },
    "/v1/analytics/overview": {
      "get": {
        "operationId": "getAnalyticsOverview",
        "summary": "Pipeline funnel overview for the caller's organization",
        "description": "Returns the records→claims→plans→executions funnel stage counts for the\ncaller's org over the `[from, to)` window (defaults to the last 30 days).\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow TypeID (wfl_…) to scope the aggregate to. Omit for the org-wide aggregate across all workflows.",
            "schema": {
              "type": "string",
              "example": "wfl_01kx2t1nkffvha1ky4zbnpff8x"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverviewOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getAnalyticsOverview"
      }
    },
    "/v1/analytics/ingestion": {
      "get": {
        "operationId": "getAnalyticsIngestion",
        "summary": "Record ingestion and usage throughput for the caller's organization",
        "description": "Returns record ingestion (by day, by origin) and usage throughput for the\ncaller's org over the `[from, to)` window (defaults to the last 30 days).\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestionOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getAnalyticsIngestion"
      }
    },
    "/v1/analytics/knowledge": {
      "get": {
        "operationId": "getAnalyticsKnowledge",
        "summary": "Claim extraction, trust, and rubric coverage for the caller's organization",
        "description": "Returns claim extraction (by day), trust-score distribution, and rubric\nbinding coverage for the caller's org over the `[from, to)` window\n(defaults to the last 30 days). Coverage reflects current state.\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getAnalyticsKnowledge"
      }
    },
    "/v1/analytics/conflicts": {
      "get": {
        "operationId": "getAnalyticsConflicts",
        "summary": "Rubric-contention analytics for the caller's organization",
        "description": "Returns the rubric-contention funnel, median time-to-resolution, and\nconflict-type mix for the caller's org over the `[from, to)` window\n(defaults to the last 30 days), bucketed by contention detection time.\n",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound (RFC3339). Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound (RFC3339). Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConflictsOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "analytics",
        "x-speakeasy-name-override": "getAnalyticsConflicts"
      }
    },
    "/v1/records": {
      "get": {
        "operationId": "listRecords",
        "summary": "List records for an inbound source",
        "description": "Lists the calling organization's records ingested from one inbound\nsource, newest first. Keyset-paginated: pass the previous page's\n`next_cursor` back as `cursor`.\n",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "source_id",
            "in": "query",
            "required": true,
            "description": "The inbound source (isrc_…) to list records for.",
            "schema": {
              "type": "string",
              "example": "isrc_01h2xcejqtf2nbrexx3vqjhp41"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum records to return (1–100, default 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor from a prior page's next_cursor.",
            "schema": {
              "type": "string",
              "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "list"
      },
      "post": {
        "operationId": "createRecord",
        "summary": "Create a record from a JSON payload",
        "description": "Creates a record from an arbitrary JSON payload sent as\nmultipart/form-data. The payload is enveloped, stored, and queued for\nclaim extraction immediately, reaching `ready` on creation.\n",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "title",
            "in": "query",
            "description": "Optional title for the record.",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Q4 ingest batch"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreateRecordBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Record"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "create"
      }
    },
    "/v1/records/{recordId}": {
      "get": {
        "operationId": "getRecordSource",
        "summary": "Get a record's provenance view",
        "description": "Returns one record's ingestion metadata, a presigned URL for the\noriginal file, and its extracted body rendered to markdown.\n\nSupply `block_path` to also resolve that block's verbatim text and\nsource page from the same body, so provenance costs no extra read. A\npath that no longer resolves is not an error — `block_text` and\n`block_page` come back null, because a stale anchor must still open its\ndocument.\n",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "recordId",
            "in": "path",
            "required": true,
            "description": "The record (rec_…) to describe.",
            "schema": {
              "type": "string",
              "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
            }
          },
          {
            "name": "block_path",
            "in": "query",
            "required": false,
            "description": "Anchor of one block within the record, as stored on a claim. Opaque\nand record-type specific: dot-separated segments for a document\n(\"b3.r2\"), an RFC 6901 JSON Pointer for a raw record\n(\"/customer/total\"). Resolution is an exact match, so an anchor from\nanother record resolves to nothing rather than failing.\n",
            "schema": {
              "type": "string",
              "example": "b3.r2"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordSource"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-name-override": "getSource"
      }
    },
    "/v1/sources": {
      "get": {
        "operationId": "listOrgSources",
        "summary": "List inbound sources for the source picker",
        "description": "Lists the calling organization's inbound sources, each enriched with its\nconnection, its latest ready record, and — when `workflow_id` is given —\nwhether it is already attached to that workflow. Powers the discovery\ncomposer's \"attach from a synced source\" picker. Sources of soft-deleted\nconnections are omitted.\n",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Workflow (wf_…) to compute attached status against. Omit on the new-policy flow.",
            "schema": {
              "type": "string",
              "example": "wf_743f63d7d31a4c0b9e1f2a3b4c5d6e7f"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListOrgSourcesOutputBody"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-speakeasy-group": "orgSources",
        "x-speakeasy-name-override": "list"
      }
    },
    "/v1/audit/events": {
      "get": {
        "operationId": "listAuditEvents",
        "x-speakeasy-group": "audit.events",
        "summary": "List audit events",
        "description": "Lists audit events for the authenticated caller's organization,\nnewest occurred_at first. Organization admins and owners can see all\nevents; ordinary members see only events whose actor_id is their user ID.\n",
        "tags": [
          "Audit"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "resource_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "record"
          },
          {
            "name": "resource_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "record.created"
          },
          {
            "name": "actor_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          {
            "name": "layer",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/AuditLayer"
            }
          },
          {
            "name": "occurred_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "occurred_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-indexed page number."
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size (1..100)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListAuditEventsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/audit/events/{eventId}": {
      "get": {
        "operationId": "getAuditEvent",
        "x-speakeasy-group": "audit.events",
        "summary": "Get an audit event",
        "description": "Retrieves a single audit event and its joined references. Organization\nadmins and owners can retrieve any event; ordinary members can retrieve\nonly their own actor events.\n",
        "tags": [
          "Audit"
        ],
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "aud_01h2xcejqtf2nbrexx3vqjhp41"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditEvent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/audit/verify": {
      "post": {
        "operationId": "verifyAuditChain",
        "x-speakeasy-group": "audit.chain",
        "summary": "Verify the audit hash chain",
        "description": "Recomputes the tamper-evidence hash chain for the authenticated\ncaller's organization and reports the first integrity break, if any.\nAdmin-only: requires organization manage permission. Sealed events are\nwalked in seq order from genesis; each event's content hash is\nrecomputed and its chain link (prev_hash) and chain_hash are checked.\nAn optional time window narrows which events are reported and counted.\n",
        "tags": [
          "Audit"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyAuditChainRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyAuditChainResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/notifications": {
      "get": {
        "operationId": "listNotifications",
        "x-speakeasy-group": "notifications",
        "summary": "List notifications",
        "description": "Lists in-app notifications for the authenticated user's current\norganization, newest occurred_at first. Organization admins and owners\ncan see all v0 notification events; ordinary members see only events\nwhose audit actor is their user ID.\n",
        "tags": [
          "Notifications"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-indexed page number."
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size (1..100)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListNotificationsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/notifications/unread-count": {
      "get": {
        "operationId": "getUnreadNotificationCount",
        "x-speakeasy-group": "notifications",
        "summary": "Get unread notification count",
        "description": "Returns the count of current user-visible notifications without a read-state row.",
        "tags": [
          "Notifications"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnreadNotificationCountResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/notifications/{auditEventId}/read": {
      "post": {
        "operationId": "markNotificationRead",
        "x-speakeasy-group": "notifications",
        "summary": "Mark a notification read",
        "description": "Marks one current user-visible notification as read. The operation is\nidempotent and returns the notification with its read_at timestamp.\n",
        "tags": [
          "Notifications"
        ],
        "parameters": [
          {
            "name": "auditEventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "aud_01h2xcejqtf2nbrexx3vqjhp41"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkNotificationReadResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/notifications/read-all": {
      "post": {
        "operationId": "markAllNotificationsRead",
        "x-speakeasy-group": "notifications",
        "summary": "Mark all notifications read",
        "description": "Marks all current user-visible v0 notifications as read.",
        "tags": [
          "Notifications"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkAllNotificationsReadResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/identity-providers": {
      "get": {
        "operationId": "listIdentityProviders",
        "summary": "List identity providers",
        "description": "Lists the SAML identity providers registered for the caller's\ncurrent organization.\n\nAuthorization: requires `organization#list_members`, which the\nSpiceDB schema grants to any organization member.\n",
        "tags": [
          "IdentityProviders"
        ],
        "x-speakeasy-group": "identityProviders",
        "x-speakeasy-name-override": "list",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of identity providers for the organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdentityProviderListOutputBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "createIdentityProvider",
        "summary": "Register an identity provider",
        "description": "Registers a SAML identity provider for the caller's current\norganization and configures it in the shared Cognito user pool.\nThe org's verified email domains are claimed so pre-login\ndiscovery can route matching users to this provider.\n\nAuthorization: requires `organization#manage` (owner or admin).\nReturns 403 when per-org SSO is not enabled for the organization,\n503 when SSO is unavailable on this deployment, and 502 when the\nupstream identity-provider configuration fails.\n",
        "tags": [
          "IdentityProviders"
        ],
        "x-speakeasy-group": "identityProviders",
        "x-speakeasy-name-override": "create",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateIdentityProviderInputBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The registered identity provider.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdentityProvider"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/identity-providers/{provider_id}": {
      "get": {
        "operationId": "getIdentityProvider",
        "summary": "Get an identity provider",
        "description": "Returns a single SAML identity provider scoped to the caller's\ncurrent organization.\n\nAuthorization: requires `organization#list_members`.\n",
        "tags": [
          "IdentityProviders"
        ],
        "x-speakeasy-group": "identityProviders",
        "x-speakeasy-name-override": "get",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "provider_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the identity provider.\nPattern: `idp_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "idp_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested identity provider.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdentityProvider"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteIdentityProvider",
        "summary": "Delete an identity provider",
        "description": "Removes a SAML identity provider from the caller's current\norganization and de-registers it from the shared Cognito user pool.\n\nAuthorization: requires `organization#manage` (owner or admin).\n",
        "tags": [
          "IdentityProviders"
        ],
        "x-speakeasy-group": "identityProviders",
        "x-speakeasy-name-override": "delete",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrgScopeHeader"
          },
          {
            "name": "provider_id",
            "in": "path",
            "required": true,
            "description": "TypedID of the identity provider to delete.\nPattern: `idp_[0-9a-hjkmnp-tv-z]{26}`\n",
            "schema": {
              "type": "string",
              "example": "idp_01h2xcejqtf2nbrexx3vqjhp41"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Bearer authentication using a factapi-issued API key\n(`ffy_<env>_<base32_uuid><base62_random>`). Cookie-based\nsessions are accepted automatically by user-facing endpoints\nbut are not surfaced as an OpenAPI auth scheme.\n"
      }
    },
    "schemas": {
      "ErrorModel": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "detail": {
            "type": "string",
            "description": "A human-readable explanation specific to this occurrence of the problem.",
            "example": "Property foo is required but is missing."
          },
          "errors": {
            "description": "Optional list of individual error details",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            }
          },
          "instance": {
            "type": "string",
            "format": "uri",
            "description": "A URI reference that identifies the specific occurrence of the problem.",
            "example": "https://example.com/error-log/abc123"
          },
          "status": {
            "type": "integer",
            "format": "int64",
            "description": "HTTP status code",
            "example": 400
          },
          "title": {
            "type": "string",
            "description": "A short, human-readable summary of the problem type. This value should not change between occurrences of the error.",
            "example": "Bad Request"
          },
          "type": {
            "type": "string",
            "format": "uri",
            "default": "about:blank",
            "description": "A URI reference to human-readable documentation for the error.",
            "example": "https://example.com/errors/example"
          }
        }
      },
      "ErrorDetail": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'"
          },
          "message": {
            "type": "string",
            "description": "Error message text"
          },
          "value": {
            "description": "The value at the given location"
          }
        }
      },
      "Organization": {
        "additionalProperties": false,
        "type": "object",
        "description": "A Factify organization — the tenancy boundary for records, members, and API keys.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique organization TypedID.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "name": {
            "type": "string",
            "description": "Human-readable organization name.",
            "example": "Factify"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the organization was created.",
            "example": "2025-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "name",
          "created_at"
        ]
      },
      "OrganizationListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "A list of organizations the caller belongs to.",
        "properties": {
          "organizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organization"
            }
          }
        },
        "required": [
          "organizations"
        ]
      },
      "Workflow": {
        "type": "object",
        "required": [
          "id",
          "name",
          "description",
          "body_md",
          "rubrics",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Workflow TypeID (wfl_…)",
            "example": "wfl_01j9z0000000000000000000a0"
          },
          "name": {
            "type": "string",
            "description": "Human-readable workflow name."
          },
          "description": {
            "type": "string",
            "description": "Human-readable workflow description. Empty string when unset."
          },
          "active_revision_id": {
            "type": "string",
            "nullable": true,
            "description": "TypeID (wrev_…) of the workflow's currently active revision. Null\nwhen no revision is active (draft-only, never activated).\n"
          },
          "body_md": {
            "type": "string",
            "description": "Markdown template body. Claim slot placeholders use `{{slot_name}}` syntax."
          },
          "rubrics": {
            "type": "array",
            "description": "Claim slot definitions for this workflow, in creation order.",
            "items": {
              "$ref": "#/components/schemas/Rubric"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Rubric": {
        "type": "object",
        "required": [
          "id",
          "slot_name",
          "label",
          "data_type",
          "binding_scope",
          "required_when",
          "trust_floor",
          "forbidden_source_origins",
          "group_key",
          "group_label",
          "display_order",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Rubric TypeID (rub_…)"
          },
          "slot_name": {
            "type": "string",
            "description": "Unique slot name within the workflow. Used to match `{{slot_name}}` placeholders in body_md.",
            "example": "party_name"
          },
          "label": {
            "type": "string",
            "description": "Display label for the slot."
          },
          "data_type": {
            "type": "string",
            "description": "Expected data type of the extracted value (e.g. \"text\", \"date\", \"number\").",
            "example": "text"
          },
          "validation_rule": {
            "type": "string",
            "description": "Optional validation expression applied to extracted values."
          },
          "object_schema": {
            "type": "string",
            "description": "JSON Schema for an object data_type slot; empty otherwise."
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RubricSource"
            },
            "description": "Where the slot's value comes from; empty == implicit claim extraction."
          },
          "description": {
            "type": "string",
            "description": "Human-readable description shown in the Workflow Room. Use extraction_hint for LLM-facing guidance."
          },
          "extraction_hint": {
            "type": "string",
            "description": "Extraction-focused guidance. Null when not set by the creator.\n",
            "nullable": true
          },
          "binding_scope": {
            "$ref": "#/components/schemas/RubricBindingScope"
          },
          "required": {
            "type": "boolean",
            "description": "Static classifier required/optional flag for completeness gates.\nWhen true, the slot must be bound before the plan is complete\n(subject to required_when gating when that predicate is set).\n",
            "default": false
          },
          "required_when": {
            "type": "string",
            "description": "CEL boolean predicate that gates whether this rubric is currently required. Empty means unconditional."
          },
          "trust_floor": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100,
            "description": "Minimum claim trust score accepted for this rubric."
          },
          "forbidden_source_origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Claim origin kinds that may not back this rubric."
          },
          "group_key": {
            "type": "string",
            "description": "Stable section identifier. Empty means ungrouped."
          },
          "group_label": {
            "type": "string",
            "description": "Human-readable section label. Empty means unlabeled."
          },
          "display_order": {
            "type": "integer",
            "format": "int32",
            "description": "Rubric ordering key within and across sections."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ClaimSlot": {
        "type": "object",
        "required": [
          "rubric_id"
        ],
        "additionalProperties": false,
        "properties": {
          "rubric_id": {
            "type": "string",
            "description": "Rubric TypeID that defines this slot."
          },
          "slot_name": {
            "type": "string",
            "description": "The rubric's declared slot name (e.g. \"invoice_ref\"). Stable\nhandle agents can use to address the slot without knowing the\nrubric_id.\n"
          },
          "claim_id": {
            "type": "string",
            "nullable": true,
            "description": "Claim TypeID bound to this slot. Null when no claim has been bound yet."
          },
          "substitution_value": {
            "type": "string",
            "nullable": true,
            "description": "The bound claim's resolved value (the same value used in\nbody_md_resolved substitution). Null when the slot is unbound.\nWorkflow bodies (L4 /execute) consume this to assemble their\ninput from bound slots without extra round-trips.\n"
          }
        }
      },
      "Plan": {
        "type": "object",
        "required": [
          "id",
          "name",
          "workflow_id",
          "activity_state",
          "claim_slots",
          "submissions",
          "created_at",
          "open_conflicts_count",
          "total_conflicts_count"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Plan TypeID (pln_…)"
          },
          "name": {
            "type": "string",
            "description": "Human-readable case name. Empty when the case has never been renamed -- clients should render a short, viewer-local default from created_at in that case."
          },
          "workflow_id": {
            "type": "string",
            "description": "Workflow TypeID."
          },
          "activity_state": {
            "type": "string",
            "enum": [
              "IDLE",
              "PROCESSING"
            ],
            "description": "Server-computed activity state, derived from submission states:\n  IDLE — no submission is currently ingesting.\n  PROCESSING — at least one submission is still ingesting.\n\nPlan lock status is conveyed separately by `locked_at`\n(null = unlocked, set = locked).\n"
          },
          "locked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the plan was locked. Null for unlocked plans."
          },
          "body_md_resolved": {
            "type": "string",
            "nullable": true,
            "description": "Workflow body_md with claim values substituted. Null until the plan is locked."
          },
          "claim_slots": {
            "type": "array",
            "description": "One entry per rubric in the parent workflow, in creation order.",
            "items": {
              "$ref": "#/components/schemas/ClaimSlot"
            }
          },
          "submissions": {
            "type": "array",
            "description": "All submissions associated with this plan.",
            "items": {
              "$ref": "#/components/schemas/Submission"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "open_conflicts_count": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "description": "Number of claim_conflicts in `open` status whose new claim was\nsubmitted to this plan. Drives the runs-list \"N conflicts\" badge\nand the per-run conflict tab count without forcing the UI to\nN+1 on /v1/conflicts per plan.\n"
          },
          "total_conflicts_count": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "description": "Total number of claim_conflicts (open + accepted + rejected) whose\nnew claim was submitted to this plan. Backs resolved-since-detection\ntelemetry and the \"0/3 unresolved\" style breakdown.\n"
          }
        }
      },
      "Submission": {
        "type": "object",
        "required": [
          "id",
          "plan_id",
          "state",
          "filename",
          "mime_type",
          "added_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Submission TypeID (sub_…)"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan TypeID."
          },
          "record_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Record UUID once the document has been ingested. Null while state is PROCESSING."
          },
          "actor_id": {
            "type": "string",
            "description": "User TypeID for the owner who created the backing record. Omitted until a backing record exists."
          },
          "actor_label": {
            "type": "string",
            "description": "Display name or email for the owner who created the backing record."
          },
          "state": {
            "type": "string",
            "enum": [
              "PROCESSING",
              "READY",
              "FAILED"
            ],
            "description": "PROCESSING — document received, ingestion in flight.\nREADY — processing finished; the agent should re-check the\n  plan's claim slots. Collapses successful extraction and\n  zero-match extraction into one terminal state.\nFAILED: the backing record could not be ingested; terminal, does\n  not block the case, surfaced to the operator as a failed resource\n  to re-upload.\n"
          },
          "filename": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Consumer-supplied opaque JSON attached at submit time. Not\ninterpreted by the API in this increment; echoed back as\nprovided. Defaults to an empty object when the caller supplies\nnone.\n"
          },
          "added_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ListWorkflowsResponse": {
        "type": "object",
        "required": [
          "workflows"
        ],
        "additionalProperties": false,
        "properties": {
          "workflows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Workflow"
            }
          }
        }
      },
      "ListPlansResponse": {
        "type": "object",
        "required": [
          "plans",
          "page",
          "page_size",
          "total"
        ],
        "additionalProperties": false,
        "properties": {
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "page": {
            "type": "integer",
            "format": "int32",
            "minimum": 1,
            "description": "1-indexed page number."
          },
          "page_size": {
            "type": "integer",
            "format": "int32",
            "minimum": 1,
            "description": "Number of plans per page."
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Total number of plans across all pages."
          }
        }
      },
      "CreateWorkflowBody": {
        "type": "object",
        "required": [
          "name",
          "rubrics"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable workflow name.",
            "example": "Employment Agreement Extraction"
          },
          "description": {
            "type": "string",
            "description": "Human-readable workflow description.",
            "default": ""
          },
          "body_md": {
            "type": "string",
            "description": "Markdown template body with `{{slot_name}}` placeholders.",
            "default": ""
          },
          "rubrics": {
            "type": "array",
            "description": "Claim slot definitions to create with this workflow.",
            "items": {
              "$ref": "#/components/schemas/CreateRubricBody"
            }
          }
        }
      },
      "CreateRubricBody": {
        "type": "object",
        "required": [
          "slot_name",
          "label",
          "data_type"
        ],
        "additionalProperties": false,
        "properties": {
          "slot_name": {
            "type": "string",
            "description": "Unique slot identifier within this workflow.",
            "example": "party_name"
          },
          "label": {
            "type": "string",
            "example": "Party Name"
          },
          "data_type": {
            "type": "string",
            "example": "text"
          },
          "validation_rule": {
            "type": "string",
            "example": "non empty strings without any numbers",
            "default": ""
          },
          "object_schema": {
            "type": "string",
            "description": "JSON Schema for an object data_type slot; empty otherwise.",
            "default": ""
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RubricSource"
            },
            "description": "Where the slot's value comes from; empty == implicit claim extraction."
          },
          "description": {
            "type": "string",
            "default": ""
          },
          "extraction_hint": {
            "type": "string",
            "description": "Extraction-focused guidance for the matching model. When set, this overrides description for Pass A matching and rubric embedding. When omitted, description is used as the fallback.\n"
          },
          "binding_scope": {
            "$ref": "#/components/schemas/RubricBindingScope",
            "default": "plan",
            "description": "Defaults to `plan` when omitted."
          },
          "required": {
            "type": "boolean",
            "description": "Static classifier required/optional flag for completeness gates.\n"
          },
          "required_when": {
            "type": "string",
            "description": "CEL boolean predicate that gates whether this rubric is currently required.",
            "default": ""
          },
          "trust_floor": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100,
            "description": "Minimum claim trust score accepted for this rubric.",
            "default": 0
          },
          "forbidden_source_origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Claim origin kinds that may not back this rubric."
          },
          "group_key": {
            "type": "string",
            "description": "Stable section identifier. Empty means ungrouped.",
            "default": ""
          },
          "group_label": {
            "type": "string",
            "description": "Human-readable section label. Empty means unlabeled.",
            "default": ""
          },
          "display_order": {
            "type": "integer",
            "format": "int32",
            "description": "Rubric ordering key within and across sections.",
            "default": 0
          }
        }
      },
      "CreatePlanBody": {
        "type": "object",
        "required": [
          "workflow_id"
        ],
        "additionalProperties": false,
        "properties": {
          "workflow_id": {
            "type": "string",
            "description": "Workflow TypeID to instantiate."
          },
          "name": {
            "type": "string",
            "description": "Optional case name override. When omitted, the case's name stays empty and clients render a short, viewer-local default from created_at."
          }
        }
      },
      "UpdatePlanBody": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "New human-readable case name. Must be non-empty."
          }
        }
      },
      "PlanRun": {
        "type": "object",
        "required": [
          "id",
          "plan_id",
          "decision_record",
          "started_at",
          "finished_at",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "PlanRun TypeID (pex_…)"
          },
          "plan_id": {
            "type": "string",
            "description": "Parent plan TypeID."
          },
          "decision_record": {
            "type": "object",
            "additionalProperties": true,
            "description": "Workflow output, captured at run time. Shape is workflow-specific;\nconsumers validate against the workflow's published output schema.\n"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ListPlanRunsResponse": {
        "type": "object",
        "required": [
          "runs",
          "page",
          "page_size",
          "total"
        ],
        "additionalProperties": false,
        "properties": {
          "runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanRun"
            }
          },
          "page": {
            "type": "integer"
          },
          "page_size": {
            "type": "integer"
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "RubricContentionScopeType": {
        "type": "string",
        "enum": [
          "workflow",
          "plan"
        ],
        "description": "Whether the contended slot is scoped to a workflow or a plan."
      },
      "Candidate": {
        "type": "object",
        "required": [
          "id",
          "contention_id",
          "candidate_claim_id",
          "kind",
          "reason",
          "created_at",
          "statement",
          "record_id",
          "block_path"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Candidate TypeID (e.g. cand_…)."
          },
          "contention_id": {
            "type": "string",
            "description": "Parent rubric contention TypeID (e.g. ctnd_…)."
          },
          "candidate_claim_id": {
            "type": "string",
            "description": "TypeID of the competing claim (e.g. clm_…)."
          },
          "kind": {
            "type": "string",
            "enum": [
              "contradiction",
              "corroboration",
              "divergence",
              "unclassified"
            ],
            "description": "Classification of the relationship between the candidate claim and the incumbent."
          },
          "subkind": {
            "type": "string",
            "nullable": true,
            "description": "Optional free-text snake_case dimension descriptor (e.g. first_name)."
          },
          "reason": {
            "type": "string",
            "description": "Human-readable explanation from the judge."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "statement": {
            "type": "string",
            "description": "The text of the candidate claim."
          },
          "record_id": {
            "type": "string",
            "description": "TypeID of the source record (e.g. rec_…)."
          },
          "block_path": {
            "type": "string",
            "description": "Path to the source block within the document (e.g. section.paragraph index)."
          },
          "trust_score": {
            "type": "integer",
            "nullable": true,
            "description": "Source-origin trust for the candidate claim (0=upload/untrusted, 100=integration), or null if not scored."
          },
          "confidence": {
            "type": "integer",
            "nullable": true,
            "description": "Judge correctness score for the candidate claim (0-90, or 100 for explicit operator approval), or null if unscored."
          },
          "confidence_reason": {
            "type": "string",
            "nullable": true,
            "description": "Correctness-judge explanation for the candidate claim score (claims.confidence_reason). Conflict UI shows this beside the confidence badge. Distinct from reason (contention enrollment note) and from reconciliation_rationale (grounding keep-why).\n"
          },
          "substitution_value": {
            "type": "string",
            "nullable": true,
            "description": "Resolved slot value from the candidate claim (context.claims.substitution_value). Null when not set."
          }
        }
      },
      "RubricContention": {
        "type": "object",
        "required": [
          "id",
          "rubric_instance_id",
          "incumbent_binding_id",
          "status",
          "detected_at",
          "scope_type",
          "rubric_id",
          "rubric_label",
          "rubric_description"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Rubric contention TypeID (e.g. ctnd_…)."
          },
          "rubric_instance_id": {
            "type": "string",
            "description": "TypeID of the slot (rubric_instance) that is contended (e.g. rin_…)."
          },
          "incumbent_binding_id": {
            "type": "string",
            "nullable": true,
            "description": "TypeID of the challenged binding, or null when a low-confidence claim is held before first bind."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "resolved"
            ]
          },
          "detected_at": {
            "type": "string",
            "format": "date-time"
          },
          "scope_type": {
            "$ref": "#/components/schemas/RubricContentionScopeType"
          },
          "plan_id": {
            "type": "string",
            "nullable": true,
            "description": "Plan TypeID (e.g. pln_…), present when scope_type=plan."
          },
          "workflow_id": {
            "type": "string",
            "nullable": true,
            "description": "Workflow TypeID (e.g. wfl_…), present when scope_type=workflow."
          },
          "rubric_id": {
            "type": "string",
            "description": "Rubric TypeID (e.g. rub_…)."
          },
          "rubric_label": {
            "type": "string",
            "description": "Display label of the rubric being contended."
          },
          "rubric_description": {
            "type": "string",
            "description": "Human-readable description of the rubric being contended."
          },
          "anchor": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "kind": {
                "type": "string",
                "description": "Type of the anchor entity: rubric"
              },
              "ref": {
                "type": "string",
                "nullable": true,
                "description": "Stable identifier of the anchor entity (rubric TypeID)."
              }
            },
            "description": "Body anchor identity, consistent with WorkflowIssue anchor shape."
          },
          "section": {
            "type": "string",
            "nullable": true,
            "description": "Markdown heading enclosing the rubric slot's first reference in the workflow draft body (display-only, for gutter dots). Null when the anchor cannot be confidently resolved: plan-scoped contentions, slot not referenced in body, no preceding heading, or body unavailable.\n"
          }
        }
      },
      "RubricContentionDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/RubricContention"
          },
          {
            "type": "object",
            "required": [
              "incumbent_claim_id",
              "candidates",
              "incumbent_statement",
              "incumbent_record_id",
              "incumbent_block_path",
              "current_suggestions"
            ],
            "additionalProperties": false,
            "properties": {
              "resolved_candidate_id": {
                "type": "string",
                "nullable": true,
                "description": "Winning candidate TypeID (e.g. cand_…). Null for reject-all or unresolved."
              },
              "resolved_by": {
                "type": "string",
                "nullable": true,
                "description": "User TypeID of the resolver (e.g. usr_…)."
              },
              "resolved_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "resolution_rationale": {
                "type": "string",
                "description": "Human-readable rationale for the resolution."
              },
              "incumbent_claim_id": {
                "type": "string",
                "nullable": true,
                "description": "Claim TypeID of the incumbent, or null for a held first claim."
              },
              "incumbent_statement": {
                "type": "string",
                "nullable": true,
                "description": "The text of the incumbent claim, or null for a held first claim."
              },
              "incumbent_record_id": {
                "type": "string",
                "nullable": true,
                "description": "TypeID of the incumbent source record, or null for a held first claim."
              },
              "incumbent_block_path": {
                "type": "string",
                "nullable": true,
                "description": "Incumbent source block path, or null for a held first claim."
              },
              "incumbent_trust_score": {
                "type": "integer",
                "nullable": true,
                "description": "Source-origin trust for the incumbent claim (0=upload/untrusted, 100=integration), or null if not scored."
              },
              "incumbent_confidence": {
                "type": "integer",
                "nullable": true,
                "description": "Judge correctness score for the incumbent claim (0-90, or 100 for explicit operator approval), or null if unscored."
              },
              "incumbent_confidence_reason": {
                "type": "string",
                "nullable": true,
                "description": "Correctness-judge explanation for the incumbent claim score (claims.confidence_reason). Conflict UI shows this beside the confidence badge. Null when held-first or unset. Distinct from candidate.reason and from reconciliation_rationale.\n"
              },
              "incumbent_substitution_value": {
                "type": "string",
                "nullable": true,
                "description": "Resolved slot value from the incumbent claim (context.claims.substitution_value). Null when not set."
              },
              "candidates": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Candidate"
                }
              },
              "current_suggestions": {
                "type": "array",
                "description": "Current advisory suggestions for this contention.",
                "items": {
                  "$ref": "#/components/schemas/RubricContentionSuggestion"
                }
              }
            }
          }
        ]
      },
      "ListRubricContentionsResponse": {
        "type": "object",
        "required": [
          "contentions",
          "is_fresh"
        ],
        "additionalProperties": false,
        "properties": {
          "contentions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RubricContention"
            }
          },
          "is_fresh": {
            "type": "boolean",
            "description": "True when the contention-detection watermark is caught up\n(contention_requested_seq == contention_processed_seq). False during\nthe debounce window or while a detection run is in progress.\n"
          }
        }
      },
      "ResolveRubricContentionBody": {
        "type": "object",
        "description": "Exactly one of `winner_candidate_id` or `reject_all` must be set.\nSetting both or neither returns 422. `resolution_rationale` is optional\nbut recommended to explain the decision.\n",
        "additionalProperties": false,
        "properties": {
          "winner_candidate_id": {
            "type": "string",
            "nullable": true,
            "description": "TypeID of the candidate to promote as the new binding (e.g. cand_…). Mutually exclusive\nwith reject_all. When set, the incumbent is replaced by the winner's claim.\n"
          },
          "reject_all": {
            "type": "boolean",
            "description": "When true, close the contention without picking a winner. The incumbent\nclaim remains bound. Mutually exclusive with winner_candidate_id.\n"
          },
          "resolution_rationale": {
            "type": "string",
            "maxLength": 2000,
            "description": "Human-readable explanation of why this resolution was chosen."
          }
        }
      },
      "WorkflowIssue": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "title",
          "summary",
          "status",
          "anchor",
          "actions"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "WorkflowIssue TypeID (wisu_…)"
          },
          "kind": {
            "type": "string",
            "description": "Issue classification: rubric_unresolved | rubric_unbound | completeness"
          },
          "subkind": {
            "type": "string",
            "nullable": true,
            "description": "Optional sub-classification (used by completeness producer)"
          },
          "title": {
            "type": "string",
            "description": "Short display title"
          },
          "summary": {
            "type": "string",
            "description": "Longer explanation"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "dismissed",
              "retired"
            ],
            "description": "Current status of the issue"
          },
          "anchor": {
            "type": "object",
            "required": [
              "kind"
            ],
            "additionalProperties": false,
            "properties": {
              "kind": {
                "type": "string",
                "description": "Type of the anchor entity: rubric | slot"
              },
              "ref": {
                "type": "string",
                "nullable": true,
                "description": "Stable identifier of the anchor entity"
              }
            }
          },
          "section": {
            "type": "string",
            "nullable": true,
            "description": "Markdown heading enclosing the anchor (display-only, for gutter dots)"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Kind-driven suggested actions: rubric_unbound→[bind]; rubric_unresolved→[add_rubric]"
          }
        }
      },
      "WorkflowIssuesResponse": {
        "type": "object",
        "required": [
          "issues",
          "detection_status",
          "is_fresh"
        ],
        "additionalProperties": false,
        "properties": {
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowIssue"
            },
            "description": "Open workflow issues"
          },
          "detection_status": {
            "type": "string",
            "enum": [
              "idle",
              "checking",
              "error"
            ],
            "description": "Current detection state for this workflow"
          },
          "last_error": {
            "type": "string",
            "nullable": true,
            "description": "Error message when detection_status is error"
          },
          "is_fresh": {
            "type": "boolean",
            "description": "True when the gap-detection watermark is caught up\n(requested_seq == processed_seq). False during the debounce window\nor while a detection run is in progress. Clients should poll while\nfalse and render a readiness indicator.\n"
          }
        }
      },
      "WorkflowSource": {
        "type": "object",
        "required": [
          "record_id",
          "provider",
          "external_id",
          "filename",
          "mime_type",
          "title",
          "last_edited_at",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "record_id": {
            "type": "string",
            "description": "Record TypeID (rec_…) of the attached source — the immutable drift baseline"
          },
          "provider": {
            "type": "string",
            "description": "Source provider (e.g. notion, gmail); empty for upload/api records with no origin identity"
          },
          "external_id": {
            "type": "string",
            "description": "The source's durable identity within its provider; empty for upload/api records"
          },
          "filename": {
            "type": "string",
            "description": "Original filename of the source record; empty when the record carries no filename (e.g. some synced records)"
          },
          "mime_type": {
            "type": "string",
            "description": "Media type of the source record (e.g. application/pdf); empty when unknown. Lets clients pick a file-type icon without a second round-trip"
          },
          "title": {
            "type": "string",
            "description": "Human source name (e.g. the Notion page title); empty when the record carries none, then clients fall back to external_id. Populated for records ingested after ENG-6842"
          },
          "last_edited_at": {
            "type": "string",
            "description": "The source's last-edited time (RFC3339, e.g. the Notion page last_edited_time); empty when unknown, then clients fall back to created_at. Plain string (not date-time) so the empty sentinel stays valid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the workflow→source edge was first captured"
          }
        }
      },
      "WorkflowSourcesResponse": {
        "type": "object",
        "required": [
          "sources"
        ],
        "additionalProperties": false,
        "properties": {
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowSource"
            },
            "description": "The sources the workflow was authored from, oldest first"
          }
        }
      },
      "CSRFTokenOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `GET /api/auth/csrf`. The `csrfToken` value is also\ndelivered as a JS-readable `csrf_token` cookie (`HttpOnly=false`)\nwith a one-hour `MaxAge`. State-changing requests authenticated by\nsession cookie must echo this value in the `X-CSRF-Token` header;\nthe cookie and header are compared and the HMAC signature is\nvalidated server-side.\n",
        "properties": {
          "csrfToken": {
            "type": "string",
            "description": "Signed CSRF token of the form `base64url(nonce).base64url(hmac(nonce))`.\nThe HMAC is computed with the server's BetterAuth secret; tokens\nminted by either apid or factapi validate against either service\nduring the migration window (dual-secret HMAC).\n",
            "example": "9d7r8b3v0vQ2.AhRR_kZl3KkpZTQXxk7vSjOdC4U"
          }
        },
        "required": [
          "csrfToken"
        ]
      },
      "SessionResponse": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `GET /api/auth/get-session`. Always returns 200,\neven for unauthenticated callers — better-auth clients treat\n`{\"user\": null, \"session\": null}` as \"no session\" rather than\nspecial-casing 401. Stale or invalid cookies are cleared on the way\nout so the client stops sending them.\n",
        "properties": {
          "user": {
            "description": "Identifying subset of the session payload. `null` when no\nsession cookie is present or the cookie has expired / been\ninvalidated server-side via the Valkey blocklist.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/SessionUser"
              },
              {
                "type": "null"
              }
            ]
          },
          "session": {
            "description": "Session metadata (id, owning user id, expiry timestamp). Omitted\nfor anonymous callers.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/SessionInfo"
              },
              {
                "type": "null"
              }
            ]
          },
          "tokens": {
            "description": "Cognito access and ID tokens minted at login. Emitted when the\nsession cookie carries an access token (OAuth, OTP, passkey);\ndev-login cookies omit this block. Clients tolerate absence\nand fall back to the session-cookie identity.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/TokenInfo"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "user"
        ]
      },
      "SessionUser": {
        "additionalProperties": false,
        "type": "object",
        "description": "User-identifying subset of the session payload. `image` is the\nCognito `picture` claim renamed to better-auth's public field name;\nthe workspace SPA reads `image`.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "User identifier (Cognito `sub`).",
            "example": "01h2xcejqtf2nbrexx3vqjhp41"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "alice@example.com"
          },
          "name": {
            "type": "string",
            "description": "Display name. Composed server-side from Cognito claims\n(`name`, falling back to `given_name`+`family_name`, then\nemail).\n",
            "example": "Alice Example"
          },
          "image": {
            "type": "string",
            "description": "Picture URL from the Cognito `picture` claim. Empty string when\nthe identity provider did not return one.\n",
            "example": "https://lh3.googleusercontent.com/a/ACg8ocK..."
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether the identity provider has verified the email address.\nCognito users created via the OTP flow are always verified;\nsocial-login users carry the IdP's claim through unchanged.\n",
            "example": true
          }
        },
        "required": [
          "id",
          "email",
          "name",
          "emailVerified"
        ]
      },
      "SessionInfo": {
        "additionalProperties": false,
        "type": "object",
        "description": "Session-identifying metadata. The `id` is the server-side session\nidentifier the blocklist keys off — sign-out writes `id` to the\nblocklist with a TTL matching `expiresAt`, so cookies replayed\nafter logout decrypt successfully but resolve as anonymous.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Server-side session identifier (random UUID).",
            "example": "1c39c0f4-0a5e-4f5e-9d3a-5b3c2f8c1f7a"
          },
          "userId": {
            "type": "string",
            "description": "Owning user id (matches `user.id`).",
            "example": "01h2xcejqtf2nbrexx3vqjhp41"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp at which the session cookie expires on the\nbrowser. Cookies are minted with a fixed two-week lifetime.\n",
            "example": "2026-05-26T12:00:00Z"
          }
        },
        "required": [
          "id",
          "userId",
          "expiresAt"
        ]
      },
      "TokenInfo": {
        "additionalProperties": false,
        "type": "object",
        "description": "Cognito tokens the workspace's better-auth client needs for refresh\nand downstream calls. Empty / absent on dev-login or\nencrypt-session-only flows.\n",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "Cognito access token (JWT). Used to call other Cognito APIs\n(passkey lifecycle, sign-out) on behalf of the user.\n",
            "example": "eyJraWQiOiJ..."
          },
          "idToken": {
            "type": "string",
            "description": "Cognito ID token (JWT). Intentionally NOT persisted into the\nsession cookie (keeps the cookie under the browser's ~4 KB\nlimit); echoed here so the SPA can decode user claims without a\nsecond round trip.\n",
            "example": "eyJraWQiOiJ..."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp at which the access token expires. Falls back\nto the session expiry when the JWT's `exp` claim cannot be\nparsed.\n",
            "example": "2026-05-12T13:00:00Z"
          }
        },
        "required": [
          "accessToken"
        ]
      },
      "SignOutOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/sign-out` and `DELETE\n/api/auth/sign-out`. Sign-out is intentionally lenient: missing,\nmalformed, or already-expired cookies still return `{\"success\":\ntrue}` with the cookie cleared. A best-effort write to the Valkey\nblocklist is logged but never surfaces as a 5xx.\n",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "success"
        ]
      },
      "OAuthTokenExchangeInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/oauth/token`. Exchanges an OAuth\nauthorization code (returned by Cognito's hosted-UI flow) for\naccess, ID, and refresh tokens via Cognito's token endpoint, using\nthe configured app-client secret server-side.\n",
        "properties": {
          "code": {
            "type": "string",
            "description": "Single-use Cognito authorization code from the hosted-UI\nredirect. The handler returns 400 for any exchange failure\nwithout revealing the underlying Cognito error.\n",
            "example": "1c39c0f4-0a5e-4f5e-9d3a-5b3c2f8c1f7a"
          },
          "redirectUri": {
            "type": "string",
            "format": "uri",
            "description": "Redirect URI the SPA registered with Cognito. Must match the\nURI sent on the authorization request byte-for-byte — Cognito\nrejects the exchange otherwise.\n",
            "example": "https://app.factify.com/login/callback"
          },
          "state": {
            "type": "string",
            "description": "Opaque value the SPA passed through the authorization\nrequest. Not consulted on this endpoint — the callback\nhandler is where `state` is read.\n"
          }
        },
        "required": [
          "code",
          "redirectUri"
        ]
      },
      "OAuthTokenExchangeOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/oauth/token`. Session and\nrefresh-token cookies are set on the response; the JSON tokens are\nreturned for SPA clients that decode user claims locally.\n",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "Cognito access token.",
            "example": "eyJraWQiOiJ..."
          },
          "idToken": {
            "type": "string",
            "description": "Cognito ID token (JWT, decodable by the SPA).",
            "example": "eyJraWQiOiJ..."
          },
          "expiresIn": {
            "type": "integer",
            "format": "int32",
            "description": "Seconds until the access token expires.",
            "example": 3600
          }
        },
        "required": [
          "accessToken",
          "idToken",
          "expiresIn"
        ]
      },
      "OTPInitiateInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/otp/initiate`. Triggers the\nCognito `USER_AUTH` flow with `PREFERRED_CHALLENGE=EMAIL_OTP`.\nFor first-time callers a Cognito user is created (Cognito\nrequires a password on sign-up even when OTP is the only method\n— the value is generated server-side and never returned);\nexisting users skip the sign-up step. Rate-limited by a stricter\nOTP limiter to slow enumeration.\n",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address to send the one-time code to.",
            "example": "alice@example.com"
          }
        },
        "required": [
          "email"
        ]
      },
      "OTPInitiateOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/otp/initiate`. The `session`\nstring is an opaque Cognito challenge handle that must be passed\nback to `POST /api/auth/otp/verify` along with the code from the\nemail.\n",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "session": {
            "type": "string",
            "description": "Opaque, short-lived Cognito challenge session token.",
            "example": "AYABeF..."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Echo of the email the code was sent to.",
            "example": "alice@example.com"
          }
        },
        "required": [
          "success",
          "session",
          "email"
        ]
      },
      "OTPVerifyInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/otp/verify`. On success the\nendpoint sets the encrypted session cookie and the path-restricted\nrefresh-token cookie, then returns the Cognito tokens in the JSON\nbody. The same OTP rate limiter applies as for initiate to cap\nverify-attempt brute force.\n",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "example": "alice@example.com"
          },
          "code": {
            "type": "string",
            "description": "One-time code from the verification email. Any verification\nfailure (wrong code, expired session, lockout) surfaces\nuniformly as 400 \"Invalid verification code\"; the handler does\nnot distinguish causes.\n",
            "example": "123456"
          },
          "session": {
            "type": "string",
            "description": "Opaque session token returned by `POST /api/auth/otp/initiate`.\nExpired sessions return 400.\n",
            "example": "AYABeF..."
          }
        },
        "required": [
          "email",
          "code",
          "session"
        ]
      },
      "OTPVerifyOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/otp/verify`.\n",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "accessToken": {
            "type": "string",
            "example": "eyJraWQiOiJ..."
          },
          "idToken": {
            "type": "string",
            "example": "eyJraWQiOiJ..."
          },
          "expiresIn": {
            "type": "integer",
            "format": "int32",
            "description": "Seconds until the access token expires.",
            "example": 3600
          }
        },
        "required": [
          "success",
          "accessToken",
          "idToken",
          "expiresIn"
        ]
      },
      "AuthInitiateInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/initiate`. Pre-authentication\nprobe that asks Cognito which methods are available for the given\nemail in a single round trip. The endpoint never reveals\nuser-existence: non-existent users and users without passkeys\nboth route to `methods: [\"otp\"]`.\n",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "example": "alice@example.com"
          }
        },
        "required": [
          "email"
        ]
      },
      "AuthInitiateOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/initiate`. Either:\n  * `methods: [\"otp\"]` — the SPA proceeds straight to the OTP flow.\n  * `methods: [\"passkey\"]` plus inline `passkey` block — the SPA\n    invokes the WebAuthn browser API with the embedded credential\n    request options and posts the result to\n    `/api/auth/passkey/authenticate/complete`.\n",
        "properties": {
          "methods": {
            "type": "array",
            "description": "Ordered list of authentication methods the SPA should attempt.\nCurrently single-element (`[\"otp\"]` or `[\"passkey\"]`); the\narray shape leaves room for hybrid flows in the future.\n",
            "items": {
              "type": "string",
              "enum": [
                "otp",
                "passkey"
              ]
            },
            "example": [
              "passkey"
            ]
          },
          "passkey": {
            "$ref": "#/components/schemas/PasskeyChallenge"
          }
        },
        "required": [
          "methods"
        ]
      },
      "PasskeyChallenge": {
        "additionalProperties": true,
        "type": "object",
        "description": "WebAuthn challenge payload for direct browser-side handling. The\n`credential_request_options` field is the raw JSON Cognito returns\n— the SPA passes it unmodified to `navigator.credentials.get()`.\nThe `session` opaque must be echoed back on\n`/api/auth/passkey/authenticate/complete`.\n",
        "properties": {
          "credential_request_options": {
            "description": "Raw `PublicKeyCredentialRequestOptions` (WebAuthn level 2)\nJSON from Cognito. Includes the challenge, RP id, and the\nuser's `allowCredentials` set.\n",
            "type": "object"
          },
          "session": {
            "type": "string",
            "description": "Opaque Cognito challenge session token.",
            "example": "AYABeF..."
          },
          "username": {
            "type": "string",
            "format": "email",
            "description": "Echo of the email so the SPA can render context.",
            "example": "alice@example.com"
          }
        }
      },
      "PasskeyAuthInitiateInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/passkey/authenticate/initiate`.\nDirect passkey challenge issuance (skipping the auth-initiate\nprobe). Returns 404 when the user has no registered passkeys so\nthe SPA falls through to OTP without opening the empty native\nWebAuthn dialog.\n",
        "properties": {
          "username": {
            "type": "string",
            "format": "email",
            "example": "alice@example.com"
          }
        },
        "required": [
          "username"
        ]
      },
      "PasskeyAuthInitiateOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/passkey/authenticate/initiate`.\nMirrors `AuthInitiateOutputBody.passkey` flattened to the top\nlevel.\n",
        "properties": {
          "credential_request_options": {
            "type": "object",
            "description": "Raw `PublicKeyCredentialRequestOptions` JSON."
          },
          "session": {
            "type": "string",
            "description": "Opaque Cognito challenge session token.",
            "example": "AYABeF..."
          },
          "username": {
            "type": "string",
            "format": "email",
            "example": "alice@example.com"
          }
        },
        "required": [
          "credential_request_options",
          "session",
          "username"
        ]
      },
      "PasskeyAuthCompleteInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/passkey/authenticate/complete`.\n`credential_response` is the raw object returned by\n`navigator.credentials.get()`; the handler forwards it to\nCognito to validate the assertion. Returns 401 `session_expired`\nwhen the challenge handle has timed out.\n",
        "properties": {
          "session": {
            "type": "string",
            "description": "Cognito challenge session token from initiate.",
            "example": "AYABeF..."
          },
          "username": {
            "type": "string",
            "format": "email",
            "description": "Email the challenge was issued for. Required when the\nCognito user pool is configured with a client secret.\n",
            "example": "alice@example.com"
          },
          "credential_response": {
            "type": "object",
            "description": "Raw `PublicKeyCredential` (WebAuthn level 2) JSON from\n`navigator.credentials.get()`. Must include the `id` field.\n"
          }
        },
        "required": [
          "session",
          "credential_response"
        ]
      },
      "PasskeyAuthCompleteOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/passkey/authenticate/complete`.\nSession cookie and refresh-token cookie are set on the response.\n",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "accessToken": {
            "type": "string",
            "example": "eyJraWQiOiJ..."
          },
          "idToken": {
            "type": "string",
            "example": "eyJraWQiOiJ..."
          },
          "expiresIn": {
            "type": "integer",
            "format": "int32",
            "example": 3600
          }
        },
        "required": [
          "success",
          "accessToken",
          "idToken",
          "expiresIn"
        ]
      },
      "PasskeyRegisterInitiateOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/passkey/register/initiate`.\nReturns `PublicKeyCredentialCreationOptions` (WebAuthn level 2)\nas raw JSON for the SPA to pass to\n`navigator.credentials.create()`.\n",
        "properties": {
          "credential_creation_options": {
            "type": "object",
            "description": "Raw `PublicKeyCredentialCreationOptions` from Cognito's\n`StartWebAuthnRegistration` call. The SPA forwards it\nverbatim to the WebAuthn browser API.\n"
          }
        },
        "required": [
          "credential_creation_options"
        ]
      },
      "PasskeyRegisterCompleteInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/passkey/register/complete`.\nCompletes WebAuthn registration with Cognito and records the\ncredential against the authenticated user. Returns 409 if the\ncredential id is already registered.\n",
        "properties": {
          "credential_response": {
            "type": "object",
            "description": "Raw `PublicKeyCredential` JSON from\n`navigator.credentials.create()`. Must include an `id` field.\n"
          },
          "display_name": {
            "type": "string",
            "maxLength": 255,
            "description": "Human-readable name shown in the credentials list (e.g.\n\"MacBook Touch ID\"). Defaults to `Passkey` when omitted.\n",
            "example": "MacBook Touch ID"
          }
        },
        "required": [
          "credential_response"
        ]
      },
      "PasskeyCredentialSummary": {
        "additionalProperties": false,
        "type": "object",
        "description": "A registered WebAuthn credential. Returned by `GET\n/api/auth/passkey/credentials` and `POST\n/api/auth/passkey/register/complete`.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Server-issued credential identifier (UUID). Use this when\ncalling `DELETE /api/auth/passkey/credentials/{id}` — not the\nraw WebAuthn credential id.\n",
            "example": "4f8c0a14-2b5c-4f5e-9d3a-5b3c2f8c1f7a"
          },
          "display_name": {
            "type": "string",
            "example": "MacBook Touch ID"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-05-12T12:00:00Z"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last successful authentication time. Null until the\ncredential is used for sign-in.\n",
            "nullable": true,
            "example": "2026-05-12T18:30:00Z"
          }
        },
        "required": [
          "id",
          "display_name",
          "created_at"
        ]
      },
      "PasskeyCredentialsListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `GET /api/auth/passkey/credentials`. Most\nrecently registered first. Empty array for users with no\npasskeys.\n",
        "properties": {
          "credentials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PasskeyCredentialSummary"
            }
          }
        },
        "required": [
          "credentials"
        ]
      },
      "RefreshTokenStoreInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /api/auth/refresh-token` (no `?action`\nquery). Stores the caller-supplied refresh token in the\npath-restricted (`Path=/api/auth`), encrypted HttpOnly cookie.\nUsed by the SPA on initial login: the Cognito refresh token\narrives in the OAuth redirect handler and is immediately handed\noff to this endpoint so it never re-enters JS-readable storage.\n\nTrust model: this endpoint is intentionally unauthenticated — it\nis reached during login before any session exists. The blast\nradius of an unauthenticated call is limited to the caller's own\nbrowser cookie, encrypted with the server's BetterAuth secret.\n",
        "properties": {
          "refreshToken": {
            "type": "string",
            "description": "Freshly-minted Cognito refresh token from a same-origin\nlogin flow. Never logged; rejected when empty.\n",
            "example": "eyJjdHkiOiJK..."
          }
        },
        "required": [
          "refreshToken"
        ]
      },
      "RefreshTokenOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Response body for `POST /api/auth/refresh-token?action=refresh`.\nCognito's refresh grant typically does not rotate the refresh\ntoken (the cookie is preserved) and returns a new ID token along\nwith the new access token; when Cognito returns no ID token the\nbody's `idToken` field is the empty string.\n",
        "properties": {
          "accessToken": {
            "type": "string",
            "example": "eyJraWQiOiJ..."
          },
          "idToken": {
            "type": "string",
            "description": "Fresh ID token. Empty string when Cognito returned none.\n",
            "example": "eyJraWQiOiJ..."
          },
          "expiresIn": {
            "type": "integer",
            "format": "int32",
            "example": 3600
          }
        },
        "required": [
          "accessToken",
          "idToken",
          "expiresIn"
        ]
      },
      "SimpleSuccessOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Generic `{\"success\": true}` response used by endpoints whose\noutcome is purely state-mutating (passkey delete, passkey prompt\ndismiss, refresh-token clear). Failure cases respond with a\nstandard error body instead.\n",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "success"
        ]
      },
      "AuditLayer": {
        "type": "string",
        "enum": [
          "record",
          "context",
          "runtime",
          "identity",
          "document",
          "inbound",
          "execution"
        ],
        "description": "Product layer that emitted the audit event. `runtime` is the Runtime (L4) layer. `inbound` is the inbound-ingest pipeline (Slack/Gmail/Notion polling, etc.). `execution` is a deprecated alias accepted on the `layer` filter for backward compatibility; responses always serve `runtime`."
      },
      "AuditEventRef": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "example": "source"
          },
          "type": {
            "type": "string",
            "example": "record"
          },
          "id": {
            "type": "string",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          }
        },
        "required": [
          "role",
          "type",
          "id"
        ]
      },
      "AuditEvent": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Audit event ID. Pattern `aud_[0-9a-hjkmnp-tv-z]{26}`.",
            "example": "aud_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "seq": {
            "type": "integer",
            "format": "int64",
            "example": 123
          },
          "organization_id": {
            "type": "string",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          },
          "actor_type": {
            "type": "string",
            "example": "user"
          },
          "actor_id": {
            "type": "string",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "action": {
            "type": "string",
            "example": "record.created"
          },
          "layer": {
            "$ref": "#/components/schemas/AuditLayer"
          },
          "resource_type": {
            "type": "string",
            "example": "record"
          },
          "resource_id": {
            "type": "string",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "dedupe_key": {
            "type": "string",
            "nullable": true
          },
          "request_id": {
            "type": "string",
            "nullable": true
          },
          "trace_id": {
            "type": "string",
            "nullable": true
          },
          "span_id": {
            "type": "string",
            "nullable": true
          },
          "schema_version": {
            "type": "integer",
            "format": "int32",
            "minimum": 1
          },
          "sensitivity": {
            "type": "string",
            "nullable": true
          },
          "visibility": {
            "type": "string",
            "nullable": true
          },
          "content_hash": {
            "type": "string"
          },
          "prev_hash": {
            "type": "string",
            "nullable": true
          },
          "chain_hash": {
            "type": "string",
            "nullable": true
          },
          "sealed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEventRef"
            },
            "description": "Joined references. Empty on list responses; populated by getAuditEvent."
          }
        },
        "required": [
          "id",
          "seq",
          "organization_id",
          "occurred_at",
          "recorded_at",
          "actor_type",
          "actor_id",
          "action",
          "layer",
          "resource_type",
          "resource_id",
          "payload",
          "schema_version",
          "content_hash",
          "refs"
        ]
      },
      "ListAuditEventsResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEvent"
            }
          },
          "page": {
            "type": "integer"
          },
          "page_size": {
            "type": "integer"
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        },
        "required": [
          "events",
          "page",
          "page_size",
          "total"
        ]
      },
      "VerifyAuditChainRequest": {
        "additionalProperties": false,
        "type": "object",
        "description": "Optional time window scoping which events are checked and reported.\nVerification always walks the org's sealed chain from genesis in seq\norder to anchor each link; the window only narrows which events are\nvalidated and counted.\n",
        "properties": {
          "occurred_after": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Only report breaks for events whose occurred_at is at or after this time."
          },
          "occurred_before": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Only verify events whose occurred_at is at or before this time."
          }
        }
      },
      "AuditChainBreak": {
        "additionalProperties": false,
        "type": "object",
        "description": "The first integrity break found while recomputing the chain.",
        "properties": {
          "seq": {
            "type": "integer",
            "format": "int64",
            "description": "Database-local append order of the broken event.",
            "example": 42
          },
          "event_id": {
            "type": "string",
            "description": "Audit event ID of the broken event. Pattern `aud_[0-9a-hjkmnp-tv-z]{26}`.",
            "example": "aud_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "reason": {
            "type": "string",
            "enum": [
              "content_hash_mismatch",
              "chain_link_mismatch",
              "chain_hash_mismatch"
            ],
            "description": "Kind of break detected. One of `content_hash_mismatch` (recomputed\ncontent hash differs from stored), `chain_link_mismatch` (prev_hash\ndoes not match the previous sealed event's chain_hash), or\n`chain_hash_mismatch` (stored chain_hash is not\nsha256(prev_hash ‖ seq ‖ content_hash)).\n",
            "example": "content_hash_mismatch"
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation of the break."
          }
        },
        "required": [
          "seq",
          "event_id",
          "reason"
        ]
      },
      "VerifyAuditChainResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "organization_id": {
            "type": "string",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "intact": {
            "type": "boolean",
            "description": "True when no break was found in the verified scope."
          },
          "verified_count": {
            "type": "integer",
            "format": "int64",
            "description": "Number of sealed events verified before the first break (or in total when intact)."
          },
          "first_break": {
            "$ref": "#/components/schemas/AuditChainBreak"
          }
        },
        "required": [
          "organization_id",
          "intact",
          "verified_count"
        ]
      },
      "Notification": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "audit_event_id": {
            "type": "string",
            "description": "Source audit event ID. Pattern `aud_[0-9a-hjkmnp-tv-z]{26}`.",
            "example": "aud_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "category": {
            "type": "string",
            "example": "execution"
          },
          "priority": {
            "$ref": "#/components/schemas/NotificationPriority"
          },
          "title": {
            "type": "string",
            "example": "Execution failed"
          },
          "body": {
            "type": "string",
            "example": "Execution failed."
          },
          "link": {
            "type": "string",
            "example": "/plans/pln_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "read_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "audit_event_id",
          "occurred_at",
          "category",
          "priority",
          "title",
          "body",
          "link",
          "read_at"
        ]
      },
      "ListNotificationsResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "notifications": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Notification"
            }
          },
          "page": {
            "type": "integer",
            "minimum": 1
          },
          "page_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          }
        },
        "required": [
          "notifications",
          "page",
          "page_size"
        ]
      },
      "UnreadNotificationCountResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "unread_count": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "maximum": 100,
            "description": "Number of unread notifications, capped at 100. A value of 100 means 100 or more; render as an overflow (e.g. \"99+\") rather than an exact figure."
          }
        },
        "required": [
          "unread_count"
        ]
      },
      "MarkNotificationReadResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "notification": {
            "$ref": "#/components/schemas/Notification"
          }
        },
        "required": [
          "notification"
        ]
      },
      "MarkAllNotificationsReadResponse": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "read_count": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Number of previously unread notifications marked read."
          }
        },
        "required": [
          "read_count"
        ]
      },
      "AnalyticsSummary": {
        "additionalProperties": false,
        "type": "object",
        "description": "Headline execution metrics for the selected window.",
        "properties": {
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Total executions in the window.",
            "example": 1280
          },
          "approve_count": {
            "type": "integer",
            "format": "int64",
            "description": "Executions whose verdict decision was `approve`.",
            "example": 960
          },
          "approval_rate": {
            "type": "number",
            "format": "double",
            "description": "`approve_count / total`, in [0, 1]. Zero when there are no\nexecutions in the window (guarded against divide-by-zero).\n",
            "example": 0.75
          },
          "avg_latency_ms": {
            "type": "number",
            "format": "double",
            "description": "Mean `finished_at - started_at` across the window, in\nmilliseconds. Zero when there are no executions.\n",
            "example": 842.5
          },
          "p50_latency_ms": {
            "type": "number",
            "format": "double",
            "description": "Median `finished_at - started_at` across the window, in\nmilliseconds. Zero when there are no executions.\n",
            "example": 650
          },
          "p95_latency_ms": {
            "type": "number",
            "format": "double",
            "description": "95th percentile `finished_at - started_at` across the window, in\nmilliseconds. Zero when there are no executions.\n",
            "example": 1940
          }
        },
        "required": [
          "total",
          "approve_count",
          "approval_rate",
          "avg_latency_ms",
          "p50_latency_ms",
          "p95_latency_ms"
        ]
      },
      "AnalyticsVolumePoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "Execution count for a single calendar day (UTC).",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "description": "Calendar date (YYYY-MM-DD).",
            "example": "2026-06-01"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "description": "Executions started on this day.",
            "example": 42
          }
        },
        "required": [
          "day",
          "count"
        ]
      },
      "AnalyticsDecisionBucket": {
        "additionalProperties": false,
        "type": "object",
        "description": "Execution count for a single verdict decision value. Decision is\nfree-string by convention; canonical values are\n`approve | review | block`, but any returned bucket is rendered.\n",
        "properties": {
          "decision": {
            "type": "string",
            "description": "Verdict decision value (`verdict ->> 'decision'`).",
            "example": "approve"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "description": "Executions with this decision.",
            "example": 960
          }
        },
        "required": [
          "decision",
          "count"
        ]
      },
      "ExecutionAnalyticsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "deprecated": true,
        "description": "Deprecated, use `RunAnalyticsOutputBody`. Aggregated execution\nanalytics for the caller's org over the requested window. Returned by\n`getExecutionAnalytics`.\n",
        "properties": {
          "summary": {
            "$ref": "#/components/schemas/AnalyticsSummary"
          },
          "volume": {
            "type": "array",
            "description": "Daily execution volume, ordered by day ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsVolumePoint"
            }
          },
          "decisions": {
            "type": "array",
            "description": "Decision breakdown, ordered by count descending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsDecisionBucket"
            }
          },
          "workflows": {
            "type": "array",
            "description": "Top workflows by execution volume, ordered descending.",
            "items": {
              "$ref": "#/components/schemas/WorkflowStat"
            }
          },
          "summary_delta": {
            "$ref": "#/components/schemas/AnalyticsSummaryDelta"
          },
          "trend": {
            "type": "array",
            "description": "Per-day volume, approvals, and latency percentiles, ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsTrendPoint"
            }
          },
          "decision_trend": {
            "type": "array",
            "description": "Per-(day, decision) counts for the decision-mix trend, day ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsDecisionMixPoint"
            }
          },
          "plans": {
            "$ref": "#/components/schemas/AnalyticsPlanStats"
          },
          "reliability": {
            "$ref": "#/components/schemas/AnalyticsReliability"
          }
        },
        "required": [
          "summary",
          "volume",
          "decisions",
          "workflows",
          "summary_delta",
          "trend",
          "decision_trend",
          "plans",
          "reliability"
        ]
      },
      "RunAnalyticsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Aggregated run analytics for the caller's org over the\nrequested window. Returned by `getRunAnalytics`.\n",
        "properties": {
          "summary": {
            "$ref": "#/components/schemas/AnalyticsSummary"
          },
          "volume": {
            "type": "array",
            "description": "Daily run volume, ordered by day ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsVolumePoint"
            }
          },
          "decisions": {
            "type": "array",
            "description": "Decision breakdown, ordered by count descending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsDecisionBucket"
            }
          },
          "workflows": {
            "type": "array",
            "description": "Top workflows by run volume, ordered descending.",
            "items": {
              "$ref": "#/components/schemas/WorkflowStat"
            }
          },
          "summary_delta": {
            "$ref": "#/components/schemas/AnalyticsSummaryDelta"
          },
          "trend": {
            "type": "array",
            "description": "Per-day volume, approvals, and latency percentiles, ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsTrendPoint"
            }
          },
          "decision_trend": {
            "type": "array",
            "description": "Per-(day, decision) counts for the decision-mix trend, day ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsDecisionMixPoint"
            }
          },
          "plans": {
            "$ref": "#/components/schemas/AnalyticsPlanStats"
          },
          "reliability": {
            "$ref": "#/components/schemas/AnalyticsReliability"
          }
        },
        "required": [
          "summary",
          "volume",
          "decisions",
          "workflows",
          "summary_delta",
          "trend",
          "decision_trend",
          "plans",
          "reliability"
        ]
      },
      "AnalyticsSummaryDelta": {
        "additionalProperties": false,
        "type": "object",
        "description": "Signed change of each headline metric vs. the immediately-preceding\nequal-length window. Rate fields are absolute point changes; the rest\nare fractional changes (0.12 = +12%). All zero when has_previous is false.\n",
        "properties": {
          "has_previous": {
            "type": "boolean",
            "description": "False when the previous window had no executions (deltas are then meaningless and zeroed).",
            "example": true
          },
          "total_pct": {
            "type": "number",
            "format": "double",
            "description": "Fractional change in total executions vs. previous window.",
            "example": 0.12
          },
          "approval_rate_points": {
            "type": "number",
            "format": "double",
            "description": "Absolute change in approval rate (percentage points, in [-1, 1]).",
            "example": 0.03
          },
          "avg_latency_pct": {
            "type": "number",
            "format": "double",
            "description": "Fractional change in average latency vs. previous window.",
            "example": -0.08
          },
          "p50_latency_pct": {
            "type": "number",
            "format": "double",
            "description": "Fractional change in p50 latency vs. previous window.",
            "example": -0.05
          },
          "p95_latency_pct": {
            "type": "number",
            "format": "double",
            "description": "Fractional change in p95 latency vs. previous window.",
            "example": 0.02
          }
        },
        "required": [
          "has_previous",
          "total_pct",
          "approval_rate_points",
          "avg_latency_pct",
          "p50_latency_pct",
          "p95_latency_pct"
        ]
      },
      "AnalyticsTrendPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One calendar day's (UTC) volume, approvals, and latency percentiles.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 42
          },
          "approve_count": {
            "type": "integer",
            "format": "int64",
            "example": 30
          },
          "p50_latency_ms": {
            "type": "number",
            "format": "double",
            "example": 650
          },
          "p95_latency_ms": {
            "type": "number",
            "format": "double",
            "example": 1940
          }
        },
        "required": [
          "day",
          "count",
          "approve_count",
          "p50_latency_ms",
          "p95_latency_ms"
        ]
      },
      "AnalyticsDecisionMixPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One (day, decision) execution count for the decision-mix trend.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "decision": {
            "type": "string",
            "example": "approve"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 30
          }
        },
        "required": [
          "day",
          "decision",
          "count"
        ]
      },
      "AnalyticsPlanCountPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One calendar day's (UTC) plan-creation count.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 18
          }
        },
        "required": [
          "day",
          "count"
        ]
      },
      "AnalyticsPlanStats": {
        "additionalProperties": false,
        "type": "object",
        "description": "Plan-creation volume and lock funnel for the window. A plan is \"locked\"\nonce locked_at is set; time-to-lock is locked_at - created_at.\n",
        "properties": {
          "created_by_day": {
            "type": "array",
            "description": "Plans opened per day, ordered by day ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsPlanCountPoint"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "example": 540
          },
          "locked_count": {
            "type": "integer",
            "format": "int64",
            "example": 410
          },
          "open_count": {
            "type": "integer",
            "format": "int64",
            "example": 130
          },
          "p50_time_to_lock_seconds": {
            "type": "number",
            "format": "double",
            "example": 3600
          },
          "p95_time_to_lock_seconds": {
            "type": "number",
            "format": "double",
            "example": 86400
          }
        },
        "required": [
          "created_by_day",
          "total",
          "locked_count",
          "open_count",
          "p50_time_to_lock_seconds",
          "p95_time_to_lock_seconds"
        ]
      },
      "AnalyticsErrorPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One calendar day's (UTC) witness call, error, and warn counts.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "call_count": {
            "type": "integer",
            "format": "int64",
            "example": 320
          },
          "error_count": {
            "type": "integer",
            "format": "int64",
            "example": 7
          },
          "warn_count": {
            "type": "integer",
            "format": "int64",
            "example": 21
          }
        },
        "required": [
          "day",
          "call_count",
          "error_count",
          "warn_count"
        ]
      },
      "AnalyticsStepStat": {
        "additionalProperties": false,
        "type": "object",
        "description": "One execution step's (witness function) volume, errors, and latency.",
        "properties": {
          "function_name": {
            "type": "string",
            "example": "classify_rubrics"
          },
          "call_count": {
            "type": "integer",
            "format": "int64",
            "example": 210
          },
          "error_count": {
            "type": "integer",
            "format": "int64",
            "example": 4
          },
          "avg_latency_ms": {
            "type": "number",
            "format": "double",
            "example": 920
          },
          "p95_latency_ms": {
            "type": "number",
            "format": "double",
            "example": 3100
          }
        },
        "required": [
          "function_name",
          "call_count",
          "error_count",
          "avg_latency_ms",
          "p95_latency_ms"
        ]
      },
      "AnalyticsReliability": {
        "additionalProperties": false,
        "type": "object",
        "description": "Execution reliability — error rate over time and the slowest/most-failing steps.",
        "properties": {
          "error_rate_by_day": {
            "type": "array",
            "description": "Witness call/error/warn counts per day, ordered ascending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsErrorPoint"
            }
          },
          "slowest_steps": {
            "type": "array",
            "description": "Top steps by p95 latency (max 10), descending.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsStepStat"
            }
          }
        },
        "required": [
          "error_rate_by_day",
          "slowest_steps"
        ]
      },
      "RecentExecutionRow": {
        "additionalProperties": false,
        "type": "object",
        "deprecated": true,
        "description": "Deprecated, use `RecentRunRow`. One row of the paginated recent-executions drill-down.",
        "properties": {
          "execution_id": {
            "type": "string",
            "description": "Plan-execution TypeID.",
            "example": "pex_01jyk0azq9e1h9v3f85n48qx6w"
          },
          "workflow_id": {
            "type": "string",
            "description": "Workflow TypeID.",
            "example": "wfl_01jyk0azq9e1h9v3f85n48qx6w"
          },
          "workflow_name": {
            "type": "string",
            "example": "Vendor invoice approval"
          },
          "decision": {
            "type": "string",
            "example": "approve"
          },
          "latency_ms": {
            "type": "number",
            "format": "double",
            "example": 842.5
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-06-16T12:00:00Z"
          }
        },
        "required": [
          "execution_id",
          "workflow_id",
          "workflow_name",
          "decision",
          "latency_ms",
          "started_at"
        ]
      },
      "RecentExecutionsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "deprecated": true,
        "description": "Deprecated, use `RecentRunsOutputBody`. One page of recent executions for the caller's org over the window.",
        "properties": {
          "rows": {
            "type": "array",
            "description": "Executions newest-first.",
            "items": {
              "$ref": "#/components/schemas/RecentExecutionRow"
            }
          },
          "page": {
            "type": "integer",
            "format": "int64",
            "description": "1-based page index echoed back.",
            "example": 1
          },
          "page_size": {
            "type": "integer",
            "format": "int64",
            "description": "Page size echoed back (after clamping).",
            "example": 25
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Total executions matching the window (for pagination).",
            "example": 1280
          }
        },
        "required": [
          "rows",
          "page",
          "page_size",
          "total"
        ]
      },
      "RecentRunRow": {
        "additionalProperties": false,
        "type": "object",
        "description": "One row of the paginated recent-runs drill-down.",
        "properties": {
          "run_id": {
            "type": "string",
            "description": "Plan-execution TypeID.",
            "example": "pex_01jyk0azq9e1h9v3f85n48qx6w"
          },
          "workflow_id": {
            "type": "string",
            "description": "Workflow TypeID.",
            "example": "wfl_01jyk0azq9e1h9v3f85n48qx6w"
          },
          "workflow_name": {
            "type": "string",
            "example": "Vendor invoice approval"
          },
          "decision": {
            "type": "string",
            "example": "approve"
          },
          "latency_ms": {
            "type": "number",
            "format": "double",
            "example": 842.5
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-06-16T12:00:00Z"
          }
        },
        "required": [
          "run_id",
          "workflow_id",
          "workflow_name",
          "decision",
          "latency_ms",
          "started_at"
        ]
      },
      "RecentRunsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "One page of recent runs for the caller's org over the window.",
        "properties": {
          "rows": {
            "type": "array",
            "description": "Runs newest-first.",
            "items": {
              "$ref": "#/components/schemas/RecentRunRow"
            }
          },
          "page": {
            "type": "integer",
            "format": "int64",
            "description": "1-based page index echoed back.",
            "example": 1
          },
          "page_size": {
            "type": "integer",
            "format": "int64",
            "description": "Page size echoed back (after clamping).",
            "example": 25
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Total runs matching the window (for pagination).",
            "example": 1280
          }
        },
        "required": [
          "rows",
          "page",
          "page_size",
          "total"
        ]
      },
      "PipelineFunnel": {
        "additionalProperties": false,
        "type": "object",
        "description": "Records→claims→plans→executions stage counts for the window.",
        "properties": {
          "records": {
            "type": "integer",
            "format": "int64",
            "example": 820
          },
          "claims": {
            "type": "integer",
            "format": "int64",
            "example": 5400
          },
          "plans_created": {
            "type": "integer",
            "format": "int64",
            "example": 540
          },
          "plans_locked": {
            "type": "integer",
            "format": "int64",
            "example": 410
          },
          "executions": {
            "type": "integer",
            "format": "int64",
            "description": "Deprecated, use `runs`. Same value.",
            "example": 1280
          },
          "runs": {
            "type": "integer",
            "format": "int64",
            "example": 1280
          }
        },
        "required": [
          "records",
          "claims",
          "plans_created",
          "plans_locked",
          "executions",
          "runs"
        ]
      },
      "OverviewOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Headline pipeline funnel for the caller's org over the window.",
        "properties": {
          "funnel": {
            "$ref": "#/components/schemas/PipelineFunnel"
          }
        },
        "required": [
          "funnel"
        ]
      },
      "RecordDayPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One day's (UTC) record ingestion with ready/failed breakdown.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 30
          },
          "ready_count": {
            "type": "integer",
            "format": "int64",
            "example": 27
          },
          "failed_count": {
            "type": "integer",
            "format": "int64",
            "example": 1
          }
        },
        "required": [
          "day",
          "count",
          "ready_count",
          "failed_count"
        ]
      },
      "OriginCount": {
        "additionalProperties": false,
        "type": "object",
        "description": "Record count for one origin provider (uploads coalesce to \"upload\").",
        "properties": {
          "origin": {
            "type": "string",
            "example": "slack"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 120
          }
        },
        "required": [
          "origin",
          "count"
        ]
      },
      "UsagePoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One day's documents-processed and api-requests tally.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "documents_processed": {
            "type": "integer",
            "format": "int64",
            "example": 42
          },
          "api_requests": {
            "type": "integer",
            "format": "int64",
            "example": 980
          }
        },
        "required": [
          "day",
          "documents_processed",
          "api_requests"
        ]
      },
      "IngestionOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Record ingestion (by day, by origin) and usage throughput.",
        "properties": {
          "records_by_day": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecordDayPoint"
            }
          },
          "records_by_origin": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OriginCount"
            }
          },
          "usage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsagePoint"
            }
          }
        },
        "required": [
          "records_by_day",
          "records_by_origin",
          "usage"
        ]
      },
      "ClaimDayPoint": {
        "additionalProperties": false,
        "type": "object",
        "description": "One day's (UTC) extracted-claim count.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "example": "2026-06-01"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 210
          }
        },
        "required": [
          "day",
          "count"
        ]
      },
      "TrustDistribution": {
        "additionalProperties": false,
        "type": "object",
        "description": "Claim trust-score histogram. `unscored` is the NULL (legacy) bucket.",
        "properties": {
          "unscored": {
            "type": "integer",
            "format": "int64",
            "example": 40
          },
          "bucket_0_24": {
            "type": "integer",
            "format": "int64",
            "example": 120
          },
          "bucket_25_49": {
            "type": "integer",
            "format": "int64",
            "example": 300
          },
          "bucket_50_74": {
            "type": "integer",
            "format": "int64",
            "example": 900
          },
          "bucket_75_100": {
            "type": "integer",
            "format": "int64",
            "example": 4000
          }
        },
        "required": [
          "unscored",
          "bucket_0_24",
          "bucket_25_49",
          "bucket_50_74",
          "bucket_75_100"
        ]
      },
      "CoverageStats": {
        "additionalProperties": false,
        "type": "object",
        "description": "Rubric-slot binding completeness (current state, not windowed).",
        "properties": {
          "total_slots": {
            "type": "integer",
            "format": "int64",
            "example": 1200
          },
          "bound_slots": {
            "type": "integer",
            "format": "int64",
            "example": 1040
          }
        },
        "required": [
          "total_slots",
          "bound_slots"
        ]
      },
      "KnowledgeOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Claim extraction, trust distribution, and rubric coverage.",
        "properties": {
          "claims_by_day": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ClaimDayPoint"
            }
          },
          "trust": {
            "$ref": "#/components/schemas/TrustDistribution"
          },
          "coverage": {
            "$ref": "#/components/schemas/CoverageStats"
          }
        },
        "required": [
          "claims_by_day",
          "trust",
          "coverage"
        ]
      },
      "ConflictStats": {
        "additionalProperties": false,
        "type": "object",
        "description": "Rubric-contention funnel + median time-to-resolution (seconds).",
        "properties": {
          "total": {
            "type": "integer",
            "format": "int64",
            "example": 86
          },
          "open_count": {
            "type": "integer",
            "format": "int64",
            "example": 14
          },
          "resolved_count": {
            "type": "integer",
            "format": "int64",
            "example": 72
          },
          "p50_resolution_seconds": {
            "type": "number",
            "format": "double",
            "example": 7200
          }
        },
        "required": [
          "total",
          "open_count",
          "resolved_count",
          "p50_resolution_seconds"
        ]
      },
      "ConflictKindCount": {
        "additionalProperties": false,
        "type": "object",
        "description": "Candidate count for one conflict kind.",
        "properties": {
          "kind": {
            "type": "string",
            "description": "contradiction | corroboration | divergence.",
            "example": "contradiction"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "example": 33
          }
        },
        "required": [
          "kind",
          "count"
        ]
      },
      "ConflictsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Rubric-contention funnel, time-to-resolution, and type mix.",
        "properties": {
          "summary": {
            "$ref": "#/components/schemas/ConflictStats"
          },
          "type_mix": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConflictKindCount"
            }
          }
        },
        "required": [
          "summary",
          "type_mix"
        ]
      },
      "UserOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Profile of the authenticated user. Returned by `GET /v1/me` and `PATCH /v1/me`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "TypedID of the user.",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "User's email address.",
            "example": "alice@factify.com"
          },
          "display_name": {
            "type": "string",
            "description": "Display name shown in the UI. Always present.",
            "example": "Alice Chen"
          },
          "given_name": {
            "type": "string",
            "nullable": true,
            "description": "Given (first) name. Null when unknown.",
            "example": "Alice"
          },
          "family_name": {
            "type": "string",
            "nullable": true,
            "description": "Family (last) name. Null when unknown.",
            "example": "Chen"
          }
        },
        "required": [
          "id",
          "email",
          "display_name"
        ]
      },
      "UpdateMeInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `PATCH /v1/me`. Optional nullable fields use\nRFC 7396 merge-patch semantics: omit to leave unchanged, send\n`null` to clear, send a value to set. `display_name` is\nrequired on every request and cannot be cleared.\n",
        "properties": {
          "display_name": {
            "type": "string",
            "description": "New display name. Required; empty string is rejected.",
            "minLength": 1,
            "maxLength": 255,
            "example": "Alice Chen"
          },
          "given_name": {
            "type": "string",
            "nullable": true,
            "description": "New given (first) name. Send `null` to clear. Empty\nstring is rejected — use `null`.\n",
            "minLength": 1,
            "maxLength": 128,
            "example": "Alice"
          },
          "family_name": {
            "type": "string",
            "nullable": true,
            "description": "New family (last) name. Send `null` to clear. Empty\nstring is rejected — use `null`.\n",
            "minLength": 1,
            "maxLength": 128,
            "example": "Chen"
          }
        },
        "required": [
          "display_name"
        ]
      },
      "UserListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Cursor-paginated page of users. Returned by `GET /v1/users` (platform\nadmin). `next` is the absolute URL of the following page; absent when\nthe current page is the last.\n",
        "properties": {
          "users": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserOutputBody"
            }
          },
          "next": {
            "type": "string",
            "format": "uri",
            "description": "Absolute URL of the next page. Absent when the current\nresponse is the last page.\n"
          }
        },
        "required": [
          "users"
        ]
      },
      "CreateUserInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Request body for `POST /v1/users` (platform admin). `email`,\n`display_name`, and `auth_sub` are required; `given_name` and\n`family_name` are optional. When `organization_id` is provided,\nthe new user is added as a member of that org with role `member`\nin the same transaction.\n",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "User's email address. Normalised to lowercase server-side.",
            "example": "alice@factify.com"
          },
          "display_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Display name shown in the UI.",
            "example": "Alice Chen"
          },
          "given_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Given (first) name. Optional; omit to leave NULL. Empty\nstring is rejected with 422.\n",
            "example": "Alice"
          },
          "family_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Family (last) name. Optional; omit to leave NULL. Empty\nstring is rejected with 422.\n",
            "example": "Chen"
          },
          "auth_sub": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "pattern": "^backoffice\\|.+",
            "description": "Auth provider's subject claim that links this row to the\nidentity backend. Must start with `backoffice|` so\nbackoffice-created rows cannot collide with Cognito-issued\nsubject claims (always bare UUIDs). The conventional payload\nis `backoffice|<sha256(email)>`. Must be globally unique.\n",
            "example": "backoffice|7f3a..."
          },
          "organization_id": {
            "type": "string",
            "pattern": "^org_[0-9a-z]{26}$",
            "description": "Organization TypedID to add the new user to as `member`.\nOptional; omit to create a user with no org membership.\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          }
        },
        "required": [
          "email",
          "display_name",
          "auth_sub"
        ]
      },
      "CreateOrganizationInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Fields required to create a new organization.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Human-readable organization name.",
            "example": "Factify"
          }
        },
        "required": [
          "name"
        ]
      },
      "UpdateOrganizationInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Fields to update on the caller's organization.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "New organization name.",
            "example": "Factify"
          }
        },
        "required": [
          "name"
        ]
      },
      "QuotaOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Document quota and usage for the caller's organization.",
        "properties": {
          "limit": {
            "type": "integer",
            "format": "int64",
            "description": "Document limit for the organization.",
            "example": 1000
          },
          "used": {
            "type": "integer",
            "format": "int64",
            "description": "Current document count for the organization.",
            "example": 150
          },
          "remaining": {
            "type": "integer",
            "format": "int64",
            "description": "Documents remaining (`limit - used`, floored at 0).",
            "example": 850
          },
          "resets_at": {
            "type": "string",
            "format": "date-time",
            "description": "Next reset timestamp (start of the next month).",
            "example": "2025-02-01T00:00:00Z"
          }
        },
        "required": [
          "limit",
          "used",
          "remaining",
          "resets_at"
        ]
      },
      "WorkflowQuotaOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Workflow/policy quota and usage for the caller's organization.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether workflow/policy creation limits are enforced for this organization.",
            "example": true
          },
          "limit": {
            "type": "integer",
            "format": "int64",
            "description": "Workflow/policy limit for the organization when enforcement is enabled.",
            "example": 10
          },
          "used": {
            "type": "integer",
            "format": "int64",
            "description": "Current non-deleted workflow count for the organization.",
            "example": 3
          },
          "remaining": {
            "type": "integer",
            "format": "int64",
            "description": "Workflows remaining (`limit - used`, floored at 0) when enforcement is enabled.",
            "example": 7
          }
        },
        "required": [
          "enabled",
          "limit",
          "used",
          "remaining"
        ]
      },
      "QuotaTier": {
        "type": "string",
        "description": "Pricing tier that selects the default monthly request/document\nlimit when an org has no explicit override.\n",
        "enum": [
          "free",
          "pro",
          "enterprise"
        ],
        "example": "free"
      },
      "OrganizationQuota": {
        "additionalProperties": false,
        "type": "object",
        "description": "Computed quota status for an organization: configured tier, the\neffective monthly limit, current period usage, and the next\nreset boundary. Returned by the platform-admin\n`getOrganizationQuota` endpoint.\n",
        "properties": {
          "organization_id": {
            "type": "string",
            "description": "TypedID of the organization the quota belongs to.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "tier": {
            "$ref": "#/components/schemas/QuotaTier"
          },
          "effective_limit": {
            "type": "integer",
            "format": "int64",
            "description": "The monthly document/request limit currently in effect for\nthe org (explicit override when set, otherwise the tier\ndefault).\n",
            "example": 1000
          },
          "current_usage": {
            "type": "integer",
            "format": "int64",
            "description": "Documents processed (or requests served) so far in the current billing period.",
            "example": 150
          },
          "remaining": {
            "type": "integer",
            "format": "int64",
            "description": "`max(effective_limit - current_usage, 0)`. The handler floors\nat zero so clients never receive a negative balance.\n",
            "example": 850
          },
          "usage_percent": {
            "type": "integer",
            "format": "int64",
            "description": "`current_usage * 100 / effective_limit`. Can exceed 100 when\nthe org has burst past its limit before the reset.\n",
            "example": 15
          },
          "is_exceeded": {
            "type": "boolean",
            "description": "Whether `current_usage >= effective_limit`.",
            "example": false
          },
          "period_start": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the current billing period (first instant of the current month, UTC).",
            "example": "2026-05-01T00:00:00Z"
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the quota resets — first instant of the next month, UTC.",
            "example": "2026-06-01T00:00:00Z"
          }
        },
        "required": [
          "organization_id",
          "tier",
          "effective_limit",
          "current_usage",
          "remaining",
          "usage_percent",
          "is_exceeded",
          "period_start",
          "reset_at"
        ]
      },
      "GetOrganizationQuotaOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Wrapper around `OrganizationQuota` so the SDK gets a stable parent struct.",
        "properties": {
          "quota": {
            "$ref": "#/components/schemas/OrganizationQuota"
          }
        },
        "required": [
          "quota"
        ]
      },
      "DailyUsage": {
        "additionalProperties": false,
        "type": "object",
        "description": "One day of aggregated usage for an organization.",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "Calendar date (YYYY-MM-DD) the usage applies to.",
            "example": "2026-05-15"
          },
          "request_count": {
            "type": "integer",
            "format": "int64",
            "description": "Total billable requests for the day. Mirrors the v1beta\n`DailyUsage.request_count` field consumed by the platform\nconsole.\n",
            "example": 42
          }
        },
        "required": [
          "date",
          "request_count"
        ]
      },
      "GetUsageHistoryOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Daily usage history for the queried organization.",
        "properties": {
          "organization_id": {
            "type": "string",
            "description": "TypedID of the organization.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "daily_usage": {
            "type": "array",
            "description": "Daily usage rows ordered by date ascending.",
            "items": {
              "$ref": "#/components/schemas/DailyUsage"
            }
          },
          "total_requests": {
            "type": "integer",
            "format": "int64",
            "description": "Sum of `request_count` across the returned rows.",
            "example": 1234
          }
        },
        "required": [
          "organization_id",
          "daily_usage",
          "total_requests"
        ]
      },
      "APIKeyQuota": {
        "additionalProperties": false,
        "type": "object",
        "description": "Per-key quota override. When `limit` is set, the key is metered\nindependently of the org-level limit (the lower of the two\napplies at request time).\n",
        "properties": {
          "api_key_id": {
            "type": "string",
            "description": "TypedID of the API key the override targets.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "limit": {
            "type": "integer",
            "format": "int64",
            "description": "Monthly request limit for this key. Absent when no per-key override is configured.",
            "example": 5000
          },
          "current_usage": {
            "type": "integer",
            "format": "int64",
            "description": "Requests served by this key in the current billing period.",
            "example": 200
          },
          "remaining": {
            "type": "integer",
            "format": "int64",
            "description": "`max(limit - current_usage, 0)`. Zero when no `limit` is\nconfigured — the org limit applies instead.\n",
            "example": 4800
          },
          "is_exceeded": {
            "type": "boolean",
            "description": "Whether the key has exceeded its per-key `limit`. Always\nfalse when no per-key limit is configured.\n",
            "example": false
          }
        },
        "required": [
          "api_key_id",
          "current_usage",
          "remaining",
          "is_exceeded"
        ]
      },
      "ListAPIKeyQuotasOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "All per-key quota overrides configured for an organization.",
        "properties": {
          "quotas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/APIKeyQuota"
            }
          }
        },
        "required": [
          "quotas"
        ]
      },
      "SetAPIKeyQuotaInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Create or update a per-key quota. The stored org is derived from\nthe API key itself, so `organization_id` is optional; when set it\nacts as a server-side guard and a mismatch surfaces as 409 Conflict.\n",
        "properties": {
          "limit": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "New monthly request limit for the key. Must be positive.",
            "example": 5000
          },
          "organization_id": {
            "type": "string",
            "description": "Optional org scope guard. When set, the server verifies the\nkey belongs to that org before writing; mismatches return 409.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          }
        },
        "required": [
          "limit"
        ]
      },
      "SetAPIKeyQuotaOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Wrapper around the resulting `APIKeyQuota`.",
        "properties": {
          "quota": {
            "$ref": "#/components/schemas/APIKeyQuota"
          }
        },
        "required": [
          "quota"
        ]
      },
      "OrgInvitation": {
        "additionalProperties": false,
        "type": "object",
        "description": "A pending invitation for an email address to join an\norganization. Invitations expire 7 days after creation.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique invitation TypedID.\nPattern: `inv_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "inv_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "Organization the invitation grants access to.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "email": {
            "type": "string",
            "description": "Email address the invitation was sent to.",
            "example": "alice@factify.com"
          },
          "role": {
            "type": "string",
            "description": "Role the recipient will be granted on accept.",
            "example": "member"
          },
          "invited_by": {
            "type": "string",
            "description": "TypedID of the user who created the invitation.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp after which the invitation token is no longer valid.",
            "example": "2025-02-15T10:30:00Z"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the invitation was created.",
            "example": "2025-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "org_id",
          "email",
          "role",
          "invited_by",
          "expires_at",
          "created_at"
        ]
      },
      "InvitationListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "A list of pending invitations for the organization.",
        "properties": {
          "items": {
            "type": "array",
            "description": "Pending invitations, ordered by creation time descending.",
            "items": {
              "$ref": "#/components/schemas/OrgInvitation"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "CreateInvitationInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Fields required to invite a new member to the organization.",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address of the person to invite.",
            "example": "alice@factify.com"
          },
          "role": {
            "type": "string",
            "description": "Role the recipient will be granted on accept.",
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "example": "member"
          }
        },
        "required": [
          "email",
          "role"
        ]
      },
      "OrgMember": {
        "additionalProperties": false,
        "type": "object",
        "description": "Membership record linking a user to an organization with a role.\nProfile fields (`email`, `display_name`, `given_name`, `family_name`)\ncome from the users table JOINed at list time, so consumers don't\nneed a second lookup to render the member roster.\n",
        "properties": {
          "org_id": {
            "type": "string",
            "description": "Organization the user is a member of.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "user_id": {
            "type": "string",
            "description": "User account TypedID.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "role": {
            "type": "string",
            "description": "The member's role within the organization.",
            "example": "member"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the user joined the organization.",
            "example": "2025-01-15T10:30:00Z"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The member's email address.",
            "example": "alice@factify.com"
          },
          "display_name": {
            "type": "string",
            "description": "The member's display name. Empty string when the user hasn't\nset one (the column has a `NOT NULL DEFAULT ''` constraint).\n",
            "example": "Alice Chen"
          },
          "given_name": {
            "type": "string",
            "nullable": true,
            "description": "First name. NULL when the user hasn't supplied one.",
            "example": "Alice"
          },
          "family_name": {
            "type": "string",
            "nullable": true,
            "description": "Last name. NULL when the user hasn't supplied one.",
            "example": "Chen"
          }
        },
        "required": [
          "org_id",
          "user_id",
          "role",
          "joined_at",
          "email",
          "display_name"
        ]
      },
      "MemberListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "A list of members in the organization.",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrgMember"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "UpdateMemberRoleInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "New role assignment for an organization member.",
        "properties": {
          "role": {
            "type": "string",
            "description": "New role for the member. Demoting the last remaining\nowner is rejected; promote another member to owner first.\n",
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "example": "admin"
          }
        },
        "required": [
          "role"
        ]
      },
      "APIKey": {
        "additionalProperties": false,
        "type": "object",
        "description": "An authentication credential for programmatic API access,\nscoped to a single organization. Keys cannot be updated;\nrotate by creating a new key and revoking the old one.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique TypedID for the API key.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "TypedID of the organization this key belongs to.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name for the key (e.g., \"Production\", \"Staging\").",
            "example": "Production Key"
          },
          "prefix": {
            "type": "string",
            "description": "Truncated key prefix for identification in UI (e.g., \"ffy_prod_01kdx2z2...\"). Safe to log;\nthe full secret is only returned once at creation time.\n",
            "example": "ffy_prod_01kdx2z2..."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the key was created.",
            "example": "2025-01-15T10:30:00Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of the last metadata update (rename, revocation, etc.).",
            "example": "2025-01-15T10:35:00Z"
          },
          "is_active": {
            "type": "boolean",
            "description": "Whether the key is currently usable for authentication.\nComputed: `is_active = (revoked_at == null) && (expires_at == null || expires_at > now())`.\n",
            "example": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiration timestamp. If set, the key becomes invalid after this time.",
            "example": "2026-01-15T10:30:00Z"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the key was revoked. Absent if the key has not been revoked."
          },
          "revoked_by": {
            "type": "string",
            "description": "TypedID of the user who revoked the key. Absent if the key\nhas not been revoked.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "revoked_comment": {
            "type": "string",
            "description": "Optional human-readable reason recorded at revocation time (audit only).",
            "example": "rotated"
          }
        },
        "required": [
          "id",
          "org_id",
          "name",
          "prefix",
          "created_at",
          "updated_at",
          "is_active"
        ]
      },
      "ListAPIKeysOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "A page of API keys for the caller's organization. Secrets are never included.",
        "properties": {
          "api_keys": {
            "type": "array",
            "description": "API keys for the organization.",
            "items": {
              "$ref": "#/components/schemas/APIKey"
            }
          }
        },
        "required": [
          "api_keys"
        ]
      },
      "CreateAPIKeyInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Fields required to create a new API key.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable name for the key (e.g. \"Production\", \"Staging\").",
            "example": "Production Key"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Optional expiration timestamp. If not set, the key does\nnot expire. When set, must be in the future and within\none year of now.\n\nSecurity note: keys without expiration should be rotated\nperiodically.\n",
            "example": "2026-07-15T10:30:00Z"
          }
        },
        "required": [
          "name"
        ]
      },
      "APIKeyCreatedOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "The newly created API key, including the full secret. The\nsecret (`key`) is only returned in this response and cannot be\nretrieved later — store it securely now.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique TypedID for the API key.\nPattern: `key_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "key_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "TypedID of the organization this key belongs to.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name for the key.",
            "example": "Production Key"
          },
          "prefix": {
            "type": "string",
            "description": "Truncated key prefix for identification in UI. Safe to log.",
            "example": "ffy_prod_01kdx2z2..."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the key was created.",
            "example": "2025-01-15T10:30:00Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of the last metadata update.",
            "example": "2025-01-15T10:35:00Z"
          },
          "is_active": {
            "type": "boolean",
            "description": "Whether the key is currently usable for authentication.",
            "example": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiration timestamp. Absent if the key does not expire.",
            "example": "2026-01-15T10:30:00Z"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the key was revoked. Absent on creation."
          },
          "revoked_by": {
            "type": "string",
            "description": "TypedID of the user who revoked the key. Absent on creation.\nPattern: `usr_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "revoked_comment": {
            "type": "string",
            "description": "Optional revocation reason. Absent on creation.",
            "example": "rotated"
          },
          "key": {
            "type": "string",
            "description": "The full API key token, format\n`ffy_{env}_{base32_uuid}{base62_random}` (per ADR-008).\nOnly returned in this response — store it securely.\n\nSECURITY: never log this value; redact it from any\npersisted request or response payloads.\n",
            "example": "ffy_prod_EXAMPLE_KEY_DO_NOT_USE"
          }
        },
        "required": [
          "key",
          "id",
          "org_id",
          "name",
          "prefix",
          "created_at",
          "updated_at",
          "is_active"
        ]
      },
      "RevokeAPIKeyBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Optional metadata for an API key revocation. The body itself is optional.",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Optional human-readable reason for revocation (audit\nonly). The revoking user's identity and timestamp are\ncaptured in audit logs separately.\n",
            "example": "rotated"
          }
        }
      },
      "Connection": {
        "additionalProperties": false,
        "type": "object",
        "description": "An inbound integration connection: one authorised account at a\nprovider (e.g. a Gmail mailbox) that records are ingested from.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique connection ID.",
            "example": "ic_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "Organization that owns this connection.",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "provider": {
            "type": "string",
            "description": "Content origin the connection authorises.",
            "enum": [
              "gmail",
              "slack",
              "salesforce",
              "notion",
              "hibob",
              "manual"
            ],
            "example": "gmail"
          },
          "status": {
            "type": "string",
            "description": "Connection lifecycle state. `pending` until the account\nowner completes the OAuth flow behind `connect_link`;\n`active` once authorised; `revoked` when the grant was\nwithdrawn at the provider or transport; `error` when the\ntransport reports a credential problem (see `last_error`).\n",
            "enum": [
              "pending",
              "active",
              "revoked",
              "error"
            ],
            "example": "active"
          },
          "external_account_id": {
            "type": "string",
            "description": "Provider-side account handle. The shape is\nprovider-dependent: a mailbox address for Gmail, an opaque\nper-authorization connection id for Notion. Empty until the\nOAuth flow completes — the account is unknowable before the\nowner authorises it.\n",
            "example": "alice@example.com"
          },
          "display_name": {
            "type": "string",
            "description": "Human-readable connection label.",
            "example": "Support mailbox"
          },
          "last_error": {
            "type": "string",
            "description": "Transport error detail for `status=error`; empty otherwise.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the connection was created.",
            "example": "2026-06-07T10:30:00Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the connection last changed state.",
            "example": "2026-06-07T10:30:00Z"
          }
        },
        "required": [
          "id",
          "org_id",
          "provider",
          "status",
          "external_account_id",
          "display_name",
          "created_at",
          "updated_at"
        ]
      },
      "ListConnectionsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "connections": {
            "type": "array",
            "description": "The organization's connections, most-recently-created\nfirst. Soft-deleted connections are omitted.\n",
            "items": {
              "$ref": "#/components/schemas/Connection"
            }
          }
        },
        "required": [
          "connections"
        ]
      },
      "CreateConnectionInputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Content origin to connect.",
            "enum": [
              "gmail",
              "slack",
              "salesforce",
              "notion",
              "hibob"
            ],
            "example": "gmail"
          },
          "display_name": {
            "type": "string",
            "description": "Optional connection label. Defaults to the provider name;\nreplaced by the account address once the OAuth flow\ncompletes.\n",
            "maxLength": 256
          }
        },
        "required": [
          "provider"
        ]
      },
      "ConnectSession": {
        "additionalProperties": false,
        "type": "object",
        "description": "A short-lived OAuth connect session. Send the account owner to\n`connect_link` (or drive an embedded Connect UI with\n`session_token`); poll `GET /v1/connections/{connection_id}/status`\nuntil the connection leaves `pending`.\n",
        "properties": {
          "session_token": {
            "type": "string",
            "description": "Token for an embedded Connect UI flow."
          },
          "connect_link": {
            "type": "string",
            "description": "Hosted authorisation URL (\"magic link\") for the same\nsession — open it in a browser to complete the OAuth flow.\n",
            "example": "https://connect.nango.dev/s/abc123"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the session (token and link) stops working.",
            "example": "2026-06-07T11:00:00Z"
          }
        },
        "required": [
          "session_token",
          "connect_link",
          "expires_at"
        ]
      },
      "CreateConnectionOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "connection": {
            "$ref": "#/components/schemas/Connection"
          },
          "connect_session": {
            "$ref": "#/components/schemas/ConnectSession"
          }
        },
        "required": [
          "connection",
          "connect_session"
        ]
      },
      "UpdateConnectionInputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string",
            "description": "New human-readable connection label. Must not be blank\n(422 otherwise).\n",
            "maxLength": 256
          }
        },
        "required": [
          "display_name"
        ]
      },
      "ConnectSessionOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "connect_session": {
            "$ref": "#/components/schemas/ConnectSession"
          }
        },
        "required": [
          "connect_session"
        ]
      },
      "InboundSource": {
        "additionalProperties": false,
        "type": "object",
        "description": "One configured ingest stream on a connection. For Gmail, a watched\nlabel on the connected mailbox (no `label_id` = the whole mailbox).\nDisabled sources stop being polled within about a minute.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique source ID.",
            "example": "isrc_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "connection_id": {
            "type": "string",
            "description": "Connection the source ingests through.",
            "example": "ic_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "Organization that owns this source.",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "provider": {
            "type": "string",
            "description": "Content origin, inherited from the connection.",
            "enum": [
              "gmail",
              "slack",
              "salesforce",
              "notion",
              "hibob",
              "manual"
            ],
            "example": "gmail"
          },
          "display_name": {
            "type": "string",
            "description": "Human-readable source label.",
            "example": "alice@example.com · contracts"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the source is being polled."
          },
          "content_kind": {
            "type": "string",
            "enum": [
              "unstructured",
              "company_ontology"
            ],
            "description": "Provider-neutral shape produced by this source."
          },
          "label_id": {
            "type": "string",
            "description": "Gmail label the source is scoped to. Absent = the whole\nmailbox. Pick from `GET …/available-labels`.\n",
            "example": "Label_7"
          },
          "backfill_since": {
            "type": "string",
            "format": "date-time",
            "example": "2026-06-01T00:00:00Z",
            "description": "Backfill horizon. Absent = forward-only — only messages\narriving after the source was created are ingested.\n"
          },
          "max_backfill_messages": {
            "type": "integer",
            "format": "int32",
            "description": "Cap on backfilled messages. Absent = uncapped."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the source was created."
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "description": "When content was last synced from this source (the coverage\nwatermark, not the last poll attempt). Absent if the source has\nnever completed a sync.\n"
          },
          "last_attempted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the latest company ontology sync attempt started."
          },
          "last_outcome": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed"
            ]
          },
          "last_error_class": {
            "type": "string",
            "enum": [
              "auth",
              "permission",
              "rate_limited",
              "transport",
              "invalid_payload",
              "too_large",
              "publish"
            ]
          }
        },
        "required": [
          "id",
          "connection_id",
          "org_id",
          "provider",
          "display_name",
          "enabled",
          "content_kind",
          "created_at"
        ]
      },
      "ListSourcesOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "sources": {
            "type": "array",
            "description": "The connection's sources, oldest first.",
            "items": {
              "$ref": "#/components/schemas/InboundSource"
            }
          }
        },
        "required": [
          "sources"
        ]
      },
      "CreateSourceInputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string",
            "description": "Optional source label. Defaults to the connected account\n(plus the label ID when one is set).\n",
            "maxLength": 256
          },
          "label_id": {
            "type": "string",
            "description": "Gmail label to scope to (from `GET …/available-labels`).\nOmit to ingest the whole mailbox.\n",
            "maxLength": 256
          },
          "backfill_since": {
            "type": "string",
            "format": "date-time",
            "description": "Opt-in backfill horizon. Omit for forward-only ingestion.\n"
          },
          "max_backfill_messages": {
            "type": "integer",
            "format": "int32",
            "minimum": 1,
            "description": "Cap on backfilled messages."
          },
          "gmail_query": {
            "type": "string",
            "maxLength": 1024,
            "description": "Gmail content filter applied per message (sender, subject, body,\nattachment presence, date). Uses a closed query grammar: bare keywords\n(match subject+body), \"quoted phrases\", from:, to:, subject:,\nhas:attachment, after:/before: (YYYY/MM/DD), - negation, OR\n(whitespace = AND). Evaluated identically on backfill and incremental\nsync. Empty = ingest every message in the label. A malformed query\nreturns 422.\n"
          }
        }
      },
      "UpdateSourceInputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Enable or disable polling for the source."
          }
        },
        "required": [
          "enabled"
        ]
      },
      "Label": {
        "additionalProperties": false,
        "type": "object",
        "description": "A provider-side label a source can be scoped to.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Provider label ID — use as `label_id` on source creation.",
            "example": "Label_7"
          },
          "name": {
            "type": "string",
            "description": "Human-readable label name.",
            "example": "contracts"
          },
          "type": {
            "type": "string",
            "description": "Provider classification (gmail: system | user).",
            "example": "user"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "ListLabelsOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "labels": {
            "type": "array",
            "description": "Labels available on the connected account.",
            "items": {
              "$ref": "#/components/schemas/Label"
            }
          }
        },
        "required": [
          "labels"
        ]
      },
      "CompanyOntologyStatus": {
        "type": "string",
        "enum": [
          "not_connected",
          "syncing",
          "ready",
          "error"
        ]
      },
      "CompanyOntologySource": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "provider"
        ]
      },
      "CompanyOntologySnapshot": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string"
          }
        },
        "required": [
          "record_id"
        ]
      },
      "CompanyOntologyOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/CompanyOntologyStatus"
          },
          "stale": {
            "type": "boolean"
          },
          "source": {
            "$ref": "#/components/schemas/CompanyOntologySource"
          },
          "snapshot": {
            "$ref": "#/components/schemas/CompanyOntologySnapshot"
          },
          "ontology": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "status",
          "stale"
        ]
      },
      "PreviewCompanyOntologyImportBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary"
          }
        },
        "required": [
          "file"
        ]
      },
      "CompanyOntologyImportSummary": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "people": {
            "type": "integer",
            "minimum": 0
          },
          "active_people": {
            "type": "integer",
            "minimum": 0
          },
          "roles": {
            "type": "integer",
            "minimum": 0
          },
          "org_units": {
            "type": "integer",
            "minimum": 0
          },
          "locations": {
            "type": "integer",
            "minimum": 0
          },
          "reporting_lines": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "people",
          "active_people",
          "roles",
          "org_units",
          "locations",
          "reporting_lines"
        ]
      },
      "CompanyOntologyImportIssue": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "row": {
            "type": "integer",
            "minimum": 0
          },
          "field": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "error",
              "warning"
            ]
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "row",
          "field",
          "severity",
          "message"
        ]
      },
      "CompanyOntologyImportRow": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "row": {
            "type": "integer",
            "minimum": 2
          },
          "employee_id": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "work_email": {
            "type": "string"
          },
          "employment_status": {
            "type": "string"
          },
          "manager_employee_id": {
            "type": "string"
          },
          "job_title": {
            "type": "string"
          },
          "org_unit": {
            "type": "string"
          },
          "org_unit_type": {
            "type": "string"
          },
          "location": {
            "type": "string"
          }
        },
        "required": [
          "row",
          "employee_id",
          "display_name",
          "work_email",
          "employment_status",
          "manager_employee_id",
          "job_title",
          "org_unit",
          "org_unit_type",
          "location"
        ]
      },
      "PreviewCompanyOntologyImportOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "preview_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "filename": {
            "type": "string"
          },
          "summary": {
            "$ref": "#/components/schemas/CompanyOntologyImportSummary"
          },
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyOntologyImportIssue"
            }
          },
          "can_commit": {
            "type": "boolean"
          },
          "rows": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/CompanyOntologyImportRow"
            }
          },
          "rows_truncated": {
            "type": "boolean"
          }
        },
        "required": [
          "preview_hash",
          "filename",
          "summary",
          "issues",
          "can_commit",
          "rows",
          "rows_truncated"
        ]
      },
      "CommitCompanyOntologyImportBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary"
          },
          "preview_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "replace_authority": {
            "type": "boolean"
          }
        },
        "required": [
          "file",
          "preview_hash",
          "replace_authority"
        ]
      },
      "CommitCompanyOntologyImportOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "snapshot_record_id": {
            "type": "string"
          },
          "changed": {
            "type": "boolean"
          },
          "summary": {
            "$ref": "#/components/schemas/CompanyOntologyImportSummary"
          }
        },
        "required": [
          "snapshot_record_id",
          "changed",
          "summary"
        ]
      },
      "RubricSource": {
        "type": "object",
        "description": "One candidate producer for a rubric slot (ENG-6227 policy chaining).",
        "required": [
          "source_type"
        ],
        "properties": {
          "source_type": {
            "type": "string",
            "enum": [
              "claim",
              "workflow",
              "operator"
            ],
            "description": "claim — extraction-derived; workflow — chained from an upstream\nworkflow's outcome; operator — exclusively operator-declared\n(via bind_fact/case UI), never a CLW Pass A/B extraction or\ncontention target (ENG-7014).\n"
          },
          "source_workflow_id": {
            "type": "string",
            "description": "Upstream workflow id; required for a workflow source, omitted for a claim source."
          }
        }
      },
      "RubricBindingScope": {
        "type": "string",
        "enum": [
          "workflow",
          "plan"
        ],
        "description": "Controls whether the rubric's claim binding is shared workflow-wide (workflow)\nor is per-plan.\n  workflow — the claim is bound once at the workflow level and snapshotted\n    into each new plan on open. OpenPlan rejects if any workflow-scoped\n    rubric has no workflow-wide binding.\n  plan — the claim is bound independently for each plan (default).\n"
      },
      "UpdateWorkflowBody": {
        "type": "object",
        "description": "Partial update of a workflow's identity metadata. Omitted fields are left unchanged. Only `name` and `description` can be changed here; the body markdown and rubrics are part of the workflow's finalized definition and are edited on the draft revision via PUT /v1/workflows/{workflowId}/draft.\n",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "New human-readable workflow name. Must be non-empty when present."
          },
          "description": {
            "type": "string",
            "description": "New workflow description. Send an empty string to clear it."
          }
        }
      },
      "WorkflowDraft": {
        "type": "object",
        "description": "A workflow's standing draft revision plus its drift review state. Returned\nby GET /v1/workflows/{workflowId}/draft. needs_review is seeded by the\ndrift trigger (ENG-6231) when a source behind the live policy changes; the\nlive (locked) active revision keeps running unchanged until the maintainer\nreconciles this draft and re-activates.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Workflow"
          },
          {
            "type": "object",
            "required": [
              "needs_review"
            ],
            "properties": {
              "needs_review": {
                "type": "boolean",
                "description": "True when this standing draft has been flagged for review (the\ndrift seed). False on a draft with no pending source drift.\n"
              },
              "needs_review_reason": {
                "type": "string",
                "nullable": true,
                "description": "Human-readable cause of the review flag (e.g. which source\nchanged). Non-null whenever needs_review is true; null otherwise.\n"
              }
            }
          }
        ]
      },
      "EditDraftRubricBody": {
        "type": "object",
        "required": [
          "label",
          "data_type",
          "binding_scope"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Rubric TypeID (rub_…). When provided, attaches an existing library\nrubric to this slot. When omitted, a new rubric is created.\n"
          },
          "label": {
            "type": "string",
            "description": "Display label for the slot."
          },
          "data_type": {
            "type": "string",
            "description": "Expected data type of the extracted value (e.g. \"text\", \"date\", \"number\").",
            "example": "text"
          },
          "validation_rule": {
            "type": "string",
            "description": "Optional validation expression applied to extracted values.",
            "default": ""
          },
          "object_schema": {
            "type": "string",
            "description": "JSON Schema for an object data_type slot; empty otherwise.",
            "default": ""
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RubricSource"
            },
            "description": "Where the slot's value comes from; empty == implicit claim extraction."
          },
          "description": {
            "type": "string",
            "description": "Human-readable description shown in the Workflow Room.",
            "default": ""
          },
          "extraction_hint": {
            "type": "string",
            "description": "Extraction-focused guidance for the matching model. Null when not set.\n",
            "nullable": true
          },
          "binding_scope": {
            "$ref": "#/components/schemas/RubricBindingScope",
            "description": "Defaults to `plan` when omitted."
          },
          "required": {
            "type": "boolean",
            "description": "Static classifier required/optional flag for completeness gates.\n"
          },
          "required_when": {
            "type": "string",
            "description": "CEL boolean predicate that gates whether this rubric is currently\nrequired. Omit to preserve the current value; send an empty string\nto clear it.\n"
          },
          "trust_floor": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100,
            "description": "Minimum claim trust score accepted for this rubric. Omit to preserve\nthe current value; send 0 to clear it.\n"
          },
          "forbidden_source_origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Claim origin kinds that may not back this rubric. Omit to preserve\nthe current value; send an empty array to clear it.\n"
          },
          "group_key": {
            "type": "string",
            "description": "Stable section identifier. Omit to preserve the current value; send\nan empty string to remove the rubric from its group.\n"
          },
          "group_label": {
            "type": "string",
            "description": "Human-readable section label. Omit to preserve the current value;\nsend an empty string to clear it.\n"
          },
          "display_order": {
            "type": "integer",
            "format": "int32",
            "description": "Rubric ordering key within and across sections. Omit to preserve the\ncurrent value; send 0 to clear it.\n"
          }
        }
      },
      "EditDraftSlotBody": {
        "type": "object",
        "required": [
          "slot_name",
          "rubric"
        ],
        "additionalProperties": false,
        "properties": {
          "slot_name": {
            "type": "string",
            "description": "Unique slot identifier within this workflow.",
            "example": "party_name"
          },
          "rubric": {
            "$ref": "#/components/schemas/EditDraftRubricBody"
          }
        }
      },
      "EditDraftBody": {
        "type": "object",
        "required": [
          "slots"
        ],
        "additionalProperties": false,
        "properties": {
          "body_md": {
            "type": "string",
            "description": "Markdown template body with `{{slot_name}}` placeholders.",
            "default": ""
          },
          "slots": {
            "type": "array",
            "description": "Complete desired set of rubric slots for the draft revision.",
            "items": {
              "$ref": "#/components/schemas/EditDraftSlotBody"
            }
          }
        }
      },
      "WorkflowSynthState": {
        "type": "string",
        "enum": [
          "pending",
          "compiling",
          "ready",
          "failed"
        ],
        "description": "State of the workflow's synth pipeline.\n  - pending   — created, synth not started\n  - compiling — taco-worker is mid-synth (LLM + build gate + S3 upload)\n  - ready     — artifact_uri set, .go source compiles\n  - failed    — error carries the reason; retried on next dispatch\n"
      },
      "WorkflowSynth": {
        "type": "object",
        "required": [
          "workflow_id",
          "state",
          "updated_at"
        ],
        "additionalProperties": false,
        "properties": {
          "workflow_id": {
            "type": "string",
            "description": "Parent workflow TypeID."
          },
          "state": {
            "$ref": "#/components/schemas/WorkflowSynthState"
          },
          "artifact_uri": {
            "type": "string",
            "nullable": true,
            "description": "s3:// URI of the synthesised .go source. Set only when state=ready."
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Last synth error. Set only when state=failed."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UpdateWorkflowSynthBody": {
        "type": "object",
        "required": [
          "state"
        ],
        "additionalProperties": false,
        "properties": {
          "state": {
            "$ref": "#/components/schemas/WorkflowSynthState"
          },
          "artifact_uri": {
            "type": "string",
            "description": "Required when state=ready."
          },
          "error": {
            "type": "string",
            "description": "Required when state=failed."
          }
        }
      },
      "FloatingRubricItem": {
        "type": "object",
        "required": [
          "rubric_id",
          "slot_name"
        ],
        "additionalProperties": false,
        "properties": {
          "rubric_id": {
            "type": "string",
            "description": "Rubric TypeID (rub_…)"
          },
          "slot_name": {
            "type": "string",
            "description": "The slot name that remains unbound."
          }
        }
      },
      "LowConfidenceClaimItem": {
        "type": "object",
        "required": [
          "rubric_id",
          "slot_name",
          "claim_id",
          "confidence",
          "min_confidence"
        ],
        "additionalProperties": false,
        "properties": {
          "rubric_id": {
            "type": "string",
            "description": "Rubric TypeID (rub_…)"
          },
          "slot_name": {
            "type": "string",
            "description": "The required slot whose bound claim is below the lock threshold."
          },
          "claim_id": {
            "type": "string",
            "description": "Claim TypeID (clm_…)"
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "Current bound value, when the claim has one."
          },
          "confidence": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100
          },
          "min_confidence": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100
          }
        }
      },
      "LockPlanConflictBody": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "floating_rubrics": {
            "type": "array",
            "description": "Plan-scoped rubric slots that have no claim_bindings row for this\nplan. Present only when the conflict is caused by floating slots;\nabsent (or empty) for other LockPlan conflict reasons (e.g.\nplan already locked, submission still PROCESSING).\n",
            "items": {
              "$ref": "#/components/schemas/FloatingRubricItem"
            }
          },
          "low_confidence_claims": {
            "type": "array",
            "description": "Required bound claims whose confidence is below the effective\nplan-lock threshold. The operator must confirm the current value or\nprovide a correction before locking can proceed.\n",
            "items": {
              "$ref": "#/components/schemas/LowConfidenceClaimItem"
            }
          }
        }
      },
      "BindPlanFactBody": {
        "type": "object",
        "required": [
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "value": {
            "type": "string",
            "description": "Operator-declared value for the rubric slot. Validated against\nthe rubric's data_type (and object_schema for object slots); must\nbe non-empty. An explicit bind always wins over the current\nincumbent, whatever its source.\n"
          }
        }
      },
      "PlanFact": {
        "type": "object",
        "description": "A rubric in the plan's pinned workflow revision with its plan-level\nbinding state. Returned by\nPOST /v1/plans/{planId}/facts/{rubricId}/binding.\n",
        "required": [
          "rubric_id",
          "slot_name",
          "label",
          "data_type",
          "bound",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "rubric_id": {
            "type": "string",
            "description": "Rubric TypeID (rub_…)"
          },
          "slot_name": {
            "type": "string",
            "description": "The rubric's declared slot name."
          },
          "label": {
            "type": "string",
            "description": "Display label for the slot."
          },
          "data_type": {
            "type": "string",
            "description": "Expected data type of the extracted value (e.g. \"text\", \"date\", \"number\")."
          },
          "bound": {
            "type": "boolean",
            "description": "Whether the rubric currently has a bound value."
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "The bound value. Null when the rubric is unbound."
          }
        }
      },
      "PlanRubricHistoryValue": {
        "type": "object",
        "required": [
          "claim_id",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "claim_id": {
            "type": "string",
            "description": "Claim TypeID (clm_…)."
          },
          "value": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The claim's committed substitution value."
          },
          "confidence": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100,
            "description": "Correctness-judge score for the claim (0-90), or 100 for explicit operator approval. Null when the judge left the claim unscored."
          },
          "reasoning": {
            "type": "string",
            "description": "Explanation for why the claim value was selected, when recorded."
          }
        }
      },
      "PlanRubricHistoryActor": {
        "type": "object",
        "required": [
          "type",
          "id",
          "label"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "user",
              "agent",
              "worker",
              "system",
              "service",
              "unknown"
            ]
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Stable actor identifier. Null for bindings created before actor capture."
          },
          "label": {
            "type": "string",
            "description": "Product-facing actor name."
          },
          "on_behalf_of": {
            "type": "string",
            "format": "email",
            "description": "Email of the user represented by an agent action, when available from record provenance."
          }
        }
      },
      "PlanRubricHistorySource": {
        "type": "object",
        "required": [
          "record_id",
          "filename",
          "mime_type",
          "block_path",
          "statement"
        ],
        "additionalProperties": false,
        "properties": {
          "record_id": {
            "type": "string",
            "description": "Record TypeID (rec_…)."
          },
          "filename": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "block_path": {
            "type": "string"
          },
          "statement": {
            "type": "string"
          }
        }
      },
      "PlanRubricHistoryEntry": {
        "type": "object",
        "required": [
          "id",
          "changed_at",
          "previous",
          "current",
          "actor",
          "source"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Claim binding TypeID (bind_…)."
          },
          "changed_at": {
            "type": "string",
            "format": "date-time"
          },
          "previous": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PlanRubricHistoryValue"
              },
              {
                "type": "null"
              }
            ]
          },
          "current": {
            "$ref": "#/components/schemas/PlanRubricHistoryValue"
          },
          "actor": {
            "$ref": "#/components/schemas/PlanRubricHistoryActor"
          },
          "source": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PlanRubricHistorySource"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "PlanRubricHistoryResponse": {
        "type": "object",
        "required": [
          "entries"
        ],
        "additionalProperties": false,
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanRubricHistoryEntry"
            }
          }
        }
      },
      "RubricContentionSuggestion": {
        "type": "object",
        "required": [
          "finding_key",
          "finding_member_claim_ids",
          "kind",
          "description",
          "rationale",
          "confidence",
          "reason",
          "metadata"
        ],
        "additionalProperties": false,
        "properties": {
          "finding_key": {
            "type": "string",
            "description": "Stable key for the advisory finding within the current generation."
          },
          "finding_member_claim_ids": {
            "type": "array",
            "description": "Candidate claim TypeIDs that participate in this finding.",
            "items": {
              "type": "string"
            }
          },
          "kind": {
            "type": "string",
            "description": "Advisory action kind, e.g. ADOPT, MERGE, SYNTHESIZE, or ESCALATE."
          },
          "description": {
            "type": "string",
            "description": "Human-readable suggestion summary."
          },
          "rationale": {
            "type": "string",
            "description": "Explanation for why the resolver suggested this action."
          },
          "confidence": {
            "type": "number",
            "format": "double",
            "minimum": 0,
            "maximum": 1
          },
          "reason": {
            "type": "string",
            "description": "Machine-readable resolver reason or status."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Resolver-supplied structured metadata for display and debugging."
          }
        }
      },
      "WorkflowStat": {
        "additionalProperties": false,
        "type": "object",
        "description": "Execution volume and approve count for one workflow.",
        "properties": {
          "workflow_id": {
            "type": "string",
            "description": "Workflow TypeID.",
            "example": "wfl_01jyk0azq9e1h9v3f85n48qx6w"
          },
          "name": {
            "type": "string",
            "description": "Workflow display name.",
            "example": "Vendor invoice approval"
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "description": "Executions for this workflow in the selected window.",
            "example": 212
          },
          "approve_count": {
            "type": "integer",
            "format": "int64",
            "description": "Executions whose verdict decision was `approve`.",
            "example": 154
          }
        },
        "required": [
          "workflow_id",
          "name",
          "count",
          "approve_count"
        ]
      },
      "Record": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "record_type": {
            "type": "string",
            "example": "raw"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "ready",
              "failed"
            ],
            "example": "ready"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2025-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "record_type",
          "status",
          "created_at"
        ]
      },
      "RecordList": {
        "type": "object",
        "properties": {
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Record"
            }
          },
          "next_cursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; pass it back as the `cursor` query\nparameter. Absent on the last page.\n",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          }
        },
        "required": [
          "records"
        ]
      },
      "CreateRecordBody": {
        "type": "object",
        "properties": {
          "payload": {
            "type": "string",
            "format": "binary",
            "description": "The JSON payload to ingest as a record."
          },
          "record_type": {
            "type": "string",
            "enum": [
              "raw"
            ],
            "default": "raw",
            "description": "Record type. Only \"raw\" is currently accepted."
          },
          "title": {
            "type": "string",
            "description": "Optional title for the record.",
            "example": "Q4 ingest batch"
          }
        },
        "required": [
          "payload"
        ]
      },
      "RecordSource": {
        "type": "object",
        "description": "A record's provenance view: ingestion metadata, a presigned URL for the\noriginal file, the extracted body rendered to markdown, and — when\n`block_path` is supplied — the verbatim text of that one block.\n",
        "properties": {
          "id": {
            "type": "string",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "record_type": {
            "type": "string",
            "example": "document"
          },
          "source_type": {
            "type": "string",
            "nullable": true,
            "example": "pdf"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "ready",
              "failed"
            ],
            "example": "ready"
          },
          "status_error": {
            "type": "string",
            "nullable": true,
            "description": "Failure detail when status is `failed`."
          },
          "origin_type": {
            "type": "string",
            "description": "How the record arrived (`upload`, `api`, a connector).",
            "example": "upload"
          },
          "origin_provider": {
            "type": "string",
            "nullable": true,
            "example": "gmail"
          },
          "created_by": {
            "type": "string",
            "example": "usr_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2025-01-15T10:30:00Z"
          },
          "source_url": {
            "type": "string",
            "nullable": true,
            "description": "Presigned URL for the original file. Null when the record has no\nstored source file.\n"
          },
          "content_markdown": {
            "type": "string",
            "nullable": true,
            "description": "The extracted body rendered to markdown."
          },
          "content_truncated": {
            "type": "boolean",
            "description": "True when content_markdown was cut to a size ceiling."
          },
          "block_text": {
            "type": "string",
            "nullable": true,
            "description": "Verbatim extracted text of the block named by `block_path` — the\ndocument's own words, which a claim's model-written statement is\nnot, so a client can locate it in a PDF's text layer. Null when no\n`block_path` was supplied or it resolved to nothing.\n"
          },
          "block_page": {
            "type": "integer",
            "nullable": true,
            "description": "That block's 1-based source page. Null for a page-less record\n(markdown, spreadsheet) or one extracted before pages were\nrecorded.\n",
            "example": 3
          }
        },
        "required": [
          "id",
          "record_type",
          "status",
          "origin_type",
          "created_by",
          "created_at",
          "content_truncated"
        ]
      },
      "OrgSourceRecord": {
        "additionalProperties": false,
        "type": "object",
        "description": "One ready record (\"page\") of a source, for the picker's expand\naffordance. Selecting it attaches this specific record. Newest first;\nrecords[0] is the source's latest ready record.\n",
        "properties": {
          "record_id": {
            "type": "string",
            "description": "The record this page attaches.",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this record was created."
          },
          "content_hash": {
            "type": "string",
            "description": "Content hash of this record, for freshness display."
          },
          "title": {
            "type": "string",
            "description": "Human title from origin metadata (Notion page title, email subject). Absent when the origin carries none.",
            "example": "Q3 Planning"
          },
          "filename": {
            "type": "string",
            "description": "Extension-bearing filename (origin metadata, provider fallback)."
          },
          "attached_record_id": {
            "type": "string",
            "description": "Set when this exact record is already attached to the in-scope workflow.",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "attached": {
            "type": "boolean",
            "description": "Whether this record is already attached to the in-scope workflow. Always false when workflow_id is omitted."
          }
        },
        "required": [
          "record_id",
          "created_at",
          "attached"
        ]
      },
      "OrgSource": {
        "additionalProperties": false,
        "type": "object",
        "description": "An inbound source enriched for the policy source picker: its connection,\nits latest ready record, and — when a workflow is in scope — whether it\nis already attached to that workflow. One row per source across the org.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique source ID.",
            "example": "isrc_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "display_name": {
            "type": "string",
            "description": "Human-readable source label."
          },
          "provider": {
            "type": "string",
            "description": "Content origin, inherited from the connection.",
            "enum": [
              "gmail",
              "slack",
              "salesforce",
              "notion",
              "hibob",
              "manual"
            ],
            "example": "notion"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the source is being polled."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the source was created."
          },
          "connection_id": {
            "type": "string",
            "description": "Connection the source ingests through.",
            "example": "ic_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "connection_display_name": {
            "type": "string",
            "description": "Human-readable label of the owning connection."
          },
          "connection_external_account_id": {
            "type": "string",
            "description": "Provider-side account handle of the owning connection: a\nmailbox address for Gmail, an opaque per-authorization\nconnection id for Notion (may be empty before OAuth\ncompletes).\n",
            "example": "alice@example.com"
          },
          "connection_status": {
            "type": "string",
            "description": "Lifecycle state of the owning connection. The picker warns on revoked/error; deleted connections are excluded.",
            "enum": [
              "pending",
              "active",
              "revoked",
              "error"
            ],
            "example": "active"
          },
          "latest_ready_record_id": {
            "type": "string",
            "description": "Latest `ready` record from this source — the record the picker attaches. Absent when none is ready yet (row renders disabled).",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "latest_ready_record_created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the latest ready record was created. Absent with no ready record."
          },
          "latest_ready_record_content_hash": {
            "type": "string",
            "description": "Content hash of the latest ready record, for freshness display. Absent with no ready record."
          },
          "latest_ready_record_title": {
            "type": "string",
            "description": "Human title of the latest ready record — the Notion page title, email subject, etc. (from origin metadata). The picker's preferred row label. Absent with no ready record or when the origin carries no title.",
            "example": "Q3 Planning"
          },
          "latest_ready_record_filename": {
            "type": "string",
            "description": "Extension-bearing filename for the latest ready record (origin metadata, with a provider fallback e.g. `.md`/`.eml`). Required by the picker accept gate. Absent with no ready record.",
            "example": "weekly-sync.md"
          },
          "attached_record_id": {
            "type": "string",
            "description": "Record of this source already attached to the in-scope workflow (`workflow_id` query param), if any. Always absent when `workflow_id` is omitted.",
            "example": "rec_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "attached": {
            "type": "boolean",
            "description": "Whether a record of this source is already attached to the in-scope workflow."
          },
          "records": {
            "type": "array",
            "description": "Every ready record (\"page\") this source has observed, newest first, capped at 50. Always present ([] when the source has no ready record).",
            "items": {
              "$ref": "#/components/schemas/OrgSourceRecord"
            }
          }
        },
        "required": [
          "id",
          "display_name",
          "provider",
          "enabled",
          "created_at",
          "connection_id",
          "connection_display_name",
          "connection_external_account_id",
          "connection_status",
          "attached",
          "records"
        ]
      },
      "ListOrgSourcesOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "sources": {
            "type": "array",
            "description": "The organization's inbound sources, enriched for the picker. Grouped by connection in the UI.",
            "items": {
              "$ref": "#/components/schemas/OrgSource"
            }
          }
        },
        "required": [
          "sources"
        ]
      },
      "NotificationPriority": {
        "type": "string",
        "enum": [
          "alert",
          "info"
        ],
        "description": "Notification priority used by the in-app feed."
      },
      "IdentityProvider": {
        "additionalProperties": false,
        "type": "object",
        "description": "A per-organization SAML identity provider (\"bring your own Active\nDirectory\" SSO). Registered in the shared Cognito user pool under\n`cognito_provider_name`.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identity-provider TypedID.\nPattern: `idp_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "idp_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "org_id": {
            "type": "string",
            "description": "Organization the provider belongs to.\nPattern: `org_[0-9a-hjkmnp-tv-z]{26}`\n",
            "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
          },
          "cognito_provider_name": {
            "type": "string",
            "description": "Provider name registered in the Cognito user pool.",
            "example": "saml-org-01h2xcejqtf2nbrexx3vqjhp41"
          },
          "type": {
            "type": "string",
            "enum": [
              "saml",
              "oidc"
            ],
            "description": "Federation protocol — `saml` (AD FS) or `oidc`.",
            "example": "saml"
          },
          "display_name": {
            "type": "string",
            "description": "Human-readable name shown in the login picker.",
            "example": "Acme Active Directory"
          },
          "status": {
            "type": "string",
            "description": "Lifecycle status. `pending` until Cognito is configured,\n`active` once usable for login, `disabled` when retired.\n",
            "example": "active"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the provider was registered.",
            "example": "2025-01-15T10:30:00Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the provider was last updated.",
            "example": "2025-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "org_id",
          "cognito_provider_name",
          "type",
          "display_name",
          "status",
          "created_at",
          "updated_at"
        ]
      },
      "IdentityProviderListOutputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "A list of identity providers for the organization.",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IdentityProvider"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "CreateIdentityProviderInputBody": {
        "additionalProperties": false,
        "type": "object",
        "description": "Fields to register a per-org SAML or OIDC identity provider. `type`\nselects the protocol; provide the matching connection fields\n(`metadata_url` for SAML, or `client_id`/`client_secret`/`issuer` for\nOIDC). Connection config is forwarded to Cognito (the system of record)\nand validated there — it is never stored or returned by this API.\n",
        "properties": {
          "display_name": {
            "type": "string",
            "minLength": 1,
            "description": "Human-readable name shown in the login picker.",
            "example": "Acme Active Directory"
          },
          "type": {
            "type": "string",
            "enum": [
              "saml",
              "oidc"
            ],
            "default": "saml",
            "description": "Federation protocol. Defaults to `saml`.",
            "example": "saml"
          },
          "domains": {
            "type": "array",
            "minItems": 1,
            "description": "Verified email domains the provider will assert. The org claims\nthese so pre-login discovery routes matching emails to this\nprovider.\n",
            "items": {
              "type": "string",
              "example": "acme.com"
            }
          },
          "metadata_url": {
            "type": "string",
            "description": "SAML only. AD FS federation-metadata URL; pushed to Cognito at\nregistration so it refreshes the signing certificate from it.\nMust be an absolute https:// URL to a public host.\n",
            "example": "https://adfs.acme.com/FederationMetadata/2007-06/FederationMetadata.xml"
          },
          "issuer": {
            "type": "string",
            "format": "uri",
            "description": "OIDC only. Issuer/discovery URL; Cognito fetches\n`{issuer}/.well-known/openid-configuration`. Must be an absolute\nhttps:// URL to a public host.\n",
            "example": "https://accounts.example.com"
          },
          "client_id": {
            "type": "string",
            "description": "OIDC only. OAuth 2.0 client id issued by the provider.",
            "example": "0oa1b2c3d4e5f6g7h8i9"
          },
          "client_secret": {
            "type": "string",
            "writeOnly": true,
            "description": "OIDC only. OAuth 2.0 client secret. Write-only: forwarded to\nCognito at registration and never persisted or returned.\n",
            "example": "********"
          },
          "scopes": {
            "type": "array",
            "description": "OIDC only. Authorization scopes. Defaults to `openid email profile`.\n",
            "items": {
              "type": "string",
              "example": "openid"
            }
          },
          "attributes_request_method": {
            "type": "string",
            "enum": [
              "GET",
              "POST"
            ],
            "description": "OIDC only. HTTP method Cognito uses to fetch user attributes.\nDefaults to `GET`; Azure AD / Entra ID requires `POST`.\n",
            "example": "GET"
          }
        },
        "required": [
          "display_name",
          "domains"
        ]
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Unauthorized",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Unauthorized",
              "status": 401,
              "detail": "Missing or invalid authentication credentials."
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Forbidden",
              "status": 403,
              "detail": "You do not have permission to perform this action on this resource."
            }
          }
        }
      },
      "BadRequest": {
        "description": "Bad Request",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Bad Request",
              "status": 400,
              "detail": "Request body is malformed or missing required fields."
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Unprocessable Entity",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Unprocessable Entity",
              "status": 422,
              "detail": "Request body failed validation.",
              "errors": [
                {
                  "location": "body.email",
                  "message": "expected string to match format 'email'",
                  "value": "not-an-email"
                }
              ]
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too Many Requests",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Too Many Requests",
              "status": 429,
              "detail": "Rate limit exceeded. Retry after the period indicated by the Retry-After header."
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal Server Error",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Internal Server Error",
              "status": 500,
              "detail": "An unexpected error occurred. Retry, and contact support if the issue persists."
            }
          }
        }
      },
      "NotFound": {
        "description": "Not Found",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Not Found",
              "status": 404,
              "detail": "The requested resource does not exist or you do not have permission to view it."
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Conflict",
              "status": 409,
              "detail": "The request conflicts with the current state of the resource."
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Payload Too Large",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Payload Too Large",
              "status": 413,
              "detail": "The uploaded file exceeds the allowed size."
            }
          }
        }
      },
      "BadGateway": {
        "description": "Bad Gateway",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Bad Gateway",
              "status": 502,
              "detail": "An upstream dependency rejected the request. Retry, and contact support if the issue persists."
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Service Unavailable",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorModel"
            },
            "example": {
              "type": "about:blank",
              "title": "Service Unavailable",
              "status": 503,
              "detail": "The endpoint is temporarily unavailable. Retry after the period indicated by the Retry-After header (if present)."
            }
          }
        }
      }
    },
    "parameters": {
      "OrgScopeHeader": {
        "name": "X-Factify-Organization-Id",
        "in": "header",
        "required": false,
        "description": "Optional. Factify-staff acting-as override: when set, factapi\nresolves the request against this organization instead of the\nsession-bound one. Honored only for callers with `@factify.com`\nemails; non-staff requests carrying this header are rejected\nwith 403. The value is a typed org id (e.g.\n`org_01h2xcejqtf2nbrexx3vqjhp41`).\n",
        "schema": {
          "type": "string",
          "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$",
          "example": "org_01h2xcejqtf2nbrexx3vqjhp41"
        }
      },
      "ConnectionID": {
        "name": "connection_id",
        "in": "path",
        "description": "Connection ID.",
        "required": true,
        "schema": {
          "type": "string",
          "example": "ic_01h2xcejqtf2nbrexx3vqjhp41"
        }
      },
      "SourceID": {
        "name": "source_id",
        "in": "path",
        "description": "Source ID.",
        "required": true,
        "schema": {
          "type": "string",
          "example": "isrc_01h2xcejqtf2nbrexx3vqjhp41"
        }
      }
    }
  }
}
