openapi: 3.0.1
info:
  title: Vectros API
  description: Enterprise-grade hybrid search and semantic memory API. Ingest documents and structured records, search across all content with hybrid BM25 + semantic ranking, and manage users, organizations, and clients.
  version: "0.29.9"
  contact:
    name: Vectros Developer Support
    url: https://docs.vectros.ai
tags:
  - name: Search
    description: Hybrid BM25 + semantic search across all indexed content
  - name: Documents
    description: Ingest text and file documents for indexing and retrieval
  - name: Records
    description: Schema-validated structured records with search indexing
  - name: Schemas
    description: Define record types with field validation and lookup indexes
  - name: Folders
    description: Organize documents and records into folder hierarchies
  - name: Identity
    description: Manage users, organizations, and clients
  - name: Inference
    description: Streaming LLM endpoints — chat, RAG, and per-document Q&A
  - name: Compliance
    description: Subject-data lifecycle — right-to-erasure and data export (GDPR/CCPA)
  - name: Auth
    description: API key validation and scoped token minting

paths:
  /v1/auth/jwks:

    get:
      tags:
      - Auth
      summary: Get the platform's JWT signing public key (JWKS)
      description: "Returns the platform's JWT signing public key in RFC 7517 JWKS\
        \ format. Use it with any JWKS-aware JWT library to verify `inv_*` invite\
        \ tokens, `st_*` scoped tokens, and other platform-signed tokens locally,\
        \ without calling back to the API for each verification. The response carries\
        \ a one-hour `Cache-Control`, so cache it and re-fetch roughly hourly rather\
        \ than on every verification. The `kid` value changes when the key rotates;\
        \ re-fetch this document whenever you encounter a token signed with an unknown\
        \ `kid`."
      operationId: getJwks
      responses:
        "200":
          description: The JWKS document containing the signing public key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JwksResponse"
        "500":
          description: The signing public key could not be retrieved. Retry the request.
      security:
        - BearerAuth: []
  /v1/admin/access-log:

    get:
      tags:
      - Auth
      summary: Query the PHI read-access (accounting-of-disclosures) log
      description: "Returns a page of per-subject PHI read-access rows: who read which\
        \ subject's PHI, when, against which record, and whether any sensitive value\
        \ was actually revealed in plaintext. Metadata only — never the PHI itself.\
        \ This is the disclosure-accounting surface from which a covered entity derives\
        \ its HIPAA §164.528 accounting of disclosures. Provide at least one query\
        \ axis: a subject (`subjectType` + `subjectId`, plus optional `clientId`)\
        \ within a `contextId` for the primary accounting query; `resourceId` within\
        \ a `contextId` for 'who read this record'; `callerKeyId` for 'what did this\
        \ credential read' (account-wide forensic); or `contextId` alone to enumerate\
        \ a whole context. `from`/`to` bound the time window. Results are scoped to\
        \ your account, derived from your token — never from input. Requires the `access-log:r`\
        \ scope."
      operationId: getAccessLog
      parameters:
      - name: subjectType
        in: query
        description: "Kind of subject to account for: `user`, `client`, or `org`."
        schema:
          type: string
        example: user
      - name: subjectId
        in: query
        description: Identifier of the data subject whose read history to return —
          the primary accounting-of-disclosures axis. Must be supplied together with
          `subjectType` (and a `contextId`).
        schema:
          type: string
        example: user_abc123
      - name: contextId
        in: query
        description: Restrict results to a single app context (the data-partition
          axis). Required for a subject or record query.
        schema:
          type: string
        example: ctx_intake
      - name: clientId
        in: query
        description: Further restrict a subject query to reads about a single nested
          client/patient.
        schema:
          type: string
        example: client_xyz789
      - name: from
        in: query
        description: "Start of the time window (ISO-8601 UTC, inclusive)."
        schema:
          type: string
        example: 2025-01-15T00:00:00Z
      - name: to
        in: query
        description: "End of the time window (ISO-8601 UTC, exclusive; defaults to\
          \ now)."
        schema:
          type: string
        example: 2025-02-15T00:00:00Z
      - name: action
        in: query
        description: "Restrict to a single action: `read`, `list`, `lookup`, `search`,\
          \ or `rag`."
        schema:
          type: string
        example: read
      - name: callerKeyId
        in: query
        description: Forensic axis — restrict to reads performed by a single credential
          (the API key / scoped token key id that made the call).
        schema:
          type: string
        example: key_abc123
      - name: resourceType
        in: query
        description: "Restrict to a single accessed resource type: a record schema\
          \ type name, or one of `document`, `search`, or `rag`."
        schema:
          type: string
        example: intake_form
      - name: resourceId
        in: query
        description: Forensic axis — restrict to reads of a single accessed record/document
          id.
        schema:
          type: string
        example: rec_456
      - name: revealedSensitive
        in: query
        description: "If true, return only reads that actually revealed a sensitive\
          \ value in plaintext."
        schema:
          type: boolean
        example: false
      - name: startFrom
        in: query
        description: Opaque cursor from a previous page's `nextCursor`; pass it back
          to fetch the next page.
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of rows to return per page (1–500; defaults to
          200).
        schema:
          type: integer
          format: int32
        example: 200
      responses:
        "200":
          description: A page of read-access rows.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadAccessLogPage"
        "400":
          description: Missing or invalid query parameters.
        "403":
          description: Your token lacks the `access-log:r` scope.
      security:
        - BearerAuth: []
  /v1/admin/keys/scoped:

    get:
      tags:
      - Auth
      summary: List your scoped API keys
      description: Lists all of your scoped API keys (`ssk_*`) across both your live
        and test environments. Revoked keys are excluded. Requires the `keys:r` scope.
      operationId: listScopedKeys
      responses:
        "200":
          description: The list of scoped API keys.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScopedKeyPage"
        "403":
          description: Your token lacks the `keys:r` scope.
      security:
        - BearerAuth: []
    post:
      tags:
      - Auth
      summary: Mint a scoped API key bound to an existing access profile
      description: "Creates a scoped API key (an `ssk_*` secret) that inherits its\
        \ permissions from an existing access profile in your account. The call is\
        \ idempotent on the combination of tenant, context, user, and key name: re-issuing\
        \ the same request returns the existing key WITHOUT re-disclosing its raw\
        \ secret. The raw key is returned ONLY in this response — store it securely,\
        \ as it cannot be retrieved again. Requires the `keys:c` scope."
      operationId: createScopedKey
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateScopedKeyRequest"
        required: true
      responses:
        "201":
          description: Key created. The raw secret is included in this response only
            and is never returned again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScopedKeyResponse"
        "200":
          description: A matching key already exists; its metadata is returned. The
            raw secret is NOT re-disclosed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScopedKeyResponse"
        "400":
          description: "Validation error — for example, a missing required field,\
            \ or your plan's active-key limit has been reached."
        "403":
          description: Your token lacks the `keys:c` scope.
        "404":
          description: "The referenced context, user, or access profile does not exist\
            \ in your account."
      security:
        - BearerAuth: []
  /v1/admin/keys/scoped/{keyId}:

    get:
      tags:
      - Auth
      summary: Get a scoped API key by its ID
      description: "Returns the metadata for a single scoped API key. The raw secret\
        \ is NOT included — it is only ever returned once, when the key is first created.\
        \ Requires the `keys:r` scope."
      operationId: getScopedKey
      parameters:
      - name: keyId
        in: path
        description: "The key's `keyId`, as returned when the key was created."
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The scoped API key's metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScopedKeyResponse"
        "403":
          description: Your token lacks the `keys:r` scope.
        "404":
          description: No such key in your account. Keys belonging to another account
            return this same response.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Auth
      summary: Revoke a scoped API key by its ID
      description: "Revokes a scoped API key. Its status changes to `revoked` and\
        \ it stops working within about 5 minutes, the maximum time authorization\
        \ is cached. Revocation is permanent. Requires the `keys:d` scope."
      operationId: revokeScopedKey
      parameters:
      - name: keyId
        in: path
        description: "The key's `keyId`, as returned when the key was created."
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The key was revoked (or was already revoked — the call is idempotent).
        "403":
          description: Your token lacks the `keys:d` scope.
        "404":
          description: No such key in your account. Keys belonging to another account
            return this same response.
      security:
        - BearerAuth: []
  /v1/admin/logs:

    get:
      tags:
      - Auth
      summary: Query your API call logs
      description: "Returns recent API call logs for your account. Each entry represents\
        \ one API request; request and response bodies are never logged. `startTime`\
        \ and `endTime` must be ISO-8601 UTC (e.g. `2025-01-15T09:00:00Z`); `endTime`\
        \ defaults to now. Filter by resource, method, key id, or context id, or set\
        \ `errorsOnly` to see only failures. Results are scoped to your account, derived\
        \ from your token — never from input. Requires the `logs:r` scope."
      operationId: getAdminLogs
      parameters:
      - name: startTime
        in: query
        description: Start of the time window (ISO-8601 UTC).
        required: true
        schema:
          type: string
        example: 2025-01-15T09:00:00Z
      - name: endTime
        in: query
        description: End of the time window (ISO-8601 UTC; defaults to now).
        schema:
          type: string
        example: 2025-01-15T10:00:00Z
      - name: resource
        in: query
        description: "Filter by resource type. One of `documents`, `records`, `search`,\
          \ `schemas`, `folders`, `clients`, `orgs`, `users`, `usage`, `auth`, `models`,\
          \ `ping`, `rag`, `chat`, `ask`, `erasure-requests`, or `export`."
        schema:
          type: string
        example: documents
      - name: method
        in: query
        description: "Filter by HTTP method (`GET`, `POST`, `PUT`, or `DELETE`)."
        schema:
          type: string
        example: POST
      - name: keyId
        in: query
        description: Filter by API key id to inspect a specific credential's traffic.
        schema:
          type: string
        example: key_abc123
      - name: contextId
        in: query
        description: Filter by app context id to inspect a specific context's traffic.
        schema:
          type: string
        example: ctx_intake
      - name: errorsOnly
        in: query
        description: "If true, return only requests with a status of 400 or higher."
        schema:
          type: boolean
      - name: limit
        in: query
        description: Maximum number of log entries to return per page (1–500; defaults
          to 200).
        schema:
          type: integer
          format: int32
        example: 200
      responses:
        "200":
          description: A page of log entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminLogsResponse"
        "400":
          description: Missing or invalid parameters.
        "403":
          description: Your token lacks the `logs:r` scope.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/profiles:

    get:
      tags:
      - Auth
      summary: List access profiles in an app context
      description: "Returns the access profiles assigned within the given app context\
        \ — in effect, who has access to this context and with what scopes. Each profile\
        \ binds a principal to either a set of inline scopes or a referenced role.\
        \ Results are paginated. Requires the `profiles:r` scope."
      operationId: listAccessProfiles
      parameters:
      - name: contextId
        in: path
        description: Your identifier for the app context.
        required: true
        schema:
          type: string
        example: myapp
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value returned by the
          previous page to fetch the next page; omit it to start from the first page.
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of results to return per page. Must be between
          1 and 100; defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: A page of access profiles.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfilePage"
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    post:
      tags:
      - Auth
      summary: Create an access profile
      description: "Creates a new access profile under the given app context. This\
        \ call is idempotent by `principalId`: if a profile with the same `principalId`\
        \ already exists, the existing profile is returned (with status 200) instead\
        \ of creating a duplicate. You must provide exactly one of `scopes` (an inline\
        \ list of scopes) or `roleId` (a reference to a role); supplying both, or\
        \ neither, is rejected. `identityOverrides` may set only `orgId` and `clientId`;\
        \ any other key (including the account identifier or `userId`) is rejected.\
        \ If you use a scoped credential, the profile's effective scopes may not exceed\
        \ your own; a root API key (`sk_`) is exempt. Requires the `profiles:c` scope."
      operationId: createAccessProfile
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessProfileRequest"
        required: true
      responses:
        "201":
          description: Access profile created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfileResponse"
        "200":
          description: A profile with this principalId already exists; the existing
            profile is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfileResponse"
        "400":
          description: "Invalid principalId, or both/neither of scopes and roleId\
            \ were provided, or an identityOverrides key is not permitted."
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts:

    get:
      tags:
      - Auth
      summary: List app contexts
      description: Returns a paginated list of the app contexts in your account. Each
        app context is a namespace that groups the access profiles and roles for one
        of your applications. Requires the `app-contexts:r` scope.
      operationId: listAppContexts
      parameters:
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value returned by the
          previous page to fetch the next page; omit it to start from the first page.
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of results to return per page. Must be between
          1 and 100; defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: A page of app contexts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppContextPage"
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    post:
      tags:
      - Auth
      summary: Create an app context
      description: "Creates a new app context. This call is idempotent by `contextId`:\
        \ if an app context with the same `contextId` already exists, the existing\
        \ app context is returned (with status 200) instead of creating a duplicate.\
        \ The reserved `contextId` value `vectros-admin` cannot be created through\
        \ this endpoint; it is provisioned automatically for your account. Requires\
        \ the `app-contexts:c` scope."
      operationId: createAppContext
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AppContextRequest"
        required: true
      responses:
        "201":
          description: App context created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppContextResponse"
        "200":
          description: A context with this contextId already exists; the existing
            context is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppContextResponse"
        "400":
          description: The contextId format is invalid.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/roles:

    get:
      tags:
      - Auth
      summary: List roles in an app context
      description: "Returns the roles defined under the given app context. A role\
        \ is a reusable, named bundle of scopes that access profiles can reference\
        \ instead of listing scopes inline. Results are paginated. Requires the `profiles:r`\
        \ scope."
      operationId: listRoles
      parameters:
      - name: contextId
        in: path
        description: Your identifier for the app context.
        required: true
        schema:
          type: string
        example: myapp
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value returned by the
          previous page to fetch the next page; omit it to start from the first page.
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of results to return per page. Must be between
          1 and 100; defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: A page of roles.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RolePage"
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    post:
      tags:
      - Auth
      summary: Create a role
      description: "Creates a new role under the given app context. This call is idempotent\
        \ by `roleId`: if a role with the same `roleId` already exists, the existing\
        \ role is returned (with status 200) instead of creating a duplicate. If you\
        \ use a scoped credential, the role's scopes may not exceed your own; a root\
        \ API key (`sk_`) is exempt. Requires the `profiles:c` scope."
      operationId: createRole
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoleRequest"
        required: true
      responses:
        "201":
          description: Role created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoleResponse"
        "200":
          description: A role with this roleId already exists; the existing role is
            returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoleResponse"
        "400":
          description: The roleId format is invalid or the scopes payload is malformed.
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/profiles/{principalId}:

    get:
      tags:
      - Auth
      summary: Get an access profile
      description: Returns a single access profile by its `principalId` within the
        given app context. Requires the `profiles:r` scope.
      operationId: getAccessProfile
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: principalId
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The requested access profile.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfileResponse"
        "404":
          description: No access profile exists with this principalId in the given
            context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    put:
      tags:
      - Auth
      summary: Update an access profile
      description: "Updates an access profile. This is a partial update: any field\
        \ you omit (or send as null) keeps its existing value. A profile must reference\
        \ either inline `scopes` or a `roleId`, never both — so setting `scopes` clears\
        \ any `roleId`, and setting `roleId` clears any inline `scopes`. The `contextId`\
        \ and `principalId` are immutable. Status changes (for example active to suspended)\
        \ take effect within about five minutes. If you use a scoped credential, the\
        \ profile's effective scopes may not exceed your own; a root API key (`sk_`)\
        \ is exempt. Requires the `profiles:u` scope."
      operationId: updateAccessProfile
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: principalId
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessProfileRequest"
        required: true
      responses:
        "200":
          description: The updated access profile.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfileResponse"
        "404":
          description: No access profile exists with this principalId in the given
            context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Auth
      summary: Delete an access profile
      description: "Deletes an access profile. Within about five minutes (the access-profile\
        \ cache lifetime), token minting for this principal in this context will be\
        \ denied. Requires the `profiles:d` scope."
      operationId: deleteAccessProfile
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: principalId
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: Access profile deleted.
        "404":
          description: No access profile exists with this principalId in the given
            context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}:

    get:
      tags:
      - Auth
      summary: Get an app context
      description: Returns a single app context by its `contextId`. Requires the `app-contexts:r`
        scope.
      operationId: getAppContext
      parameters:
      - name: contextId
        in: path
        description: Your identifier for the app context.
        required: true
        schema:
          type: string
        example: myapp
      responses:
        "200":
          description: The requested app context.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppContextResponse"
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    put:
      tags:
      - Auth
      summary: Update an app context
      description: "Updates the name and/or description of an app context. This is\
        \ a partial update: any field you omit (or send as null) keeps its existing\
        \ value. The `contextId` is immutable and is taken from the URL path, so any\
        \ `contextId` in the request body is ignored. Requires the `app-contexts:u`\
        \ scope."
      operationId: updateAppContext
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AppContextRequest"
        required: true
      responses:
        "200":
          description: The updated app context.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppContextResponse"
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Auth
      summary: Delete an app context
      description: "Permanently deletes an app context and everything in it — every\
        \ record, document, folder, schema, role, and access profile belonging to\
        \ the context. This is irreversible. The deletion runs asynchronously: the\
        \ call returns 202 immediately and the context's data drains in the background.\
        \ Poll the context's `status` field to observe when the teardown completes\
        \ (`purging` while draining, then `deleted`). To guard against accidental\
        \ deletion, you must echo the contextId back in the `confirm` query parameter\
        \ (`?confirm={contextId}`). The reserved `default` and `vectros-admin` contexts\
        \ cannot be deleted. This operation requires a root API key (one beginning\
        \ with `sk_`): no scoped credential, not even one with full wildcard (`*`)\
        \ scope, can trigger this teardown."
      operationId: deleteAppContext
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: confirm
        in: query
        description: "Confirmation token. Must exactly equal the contextId being deleted;\
          \ the request is rejected with 400 otherwise. This guards against accidental,\
          \ irreversible deletion."
        schema:
          type: string
      responses:
        "202":
          description: Teardown accepted; the context's data is draining in the background.
        "400":
          description: The `confirm` query parameter is missing or does not match
            the contextId.
        "404":
          description: No app context exists with this contextId.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/roles/{roleId}:

    get:
      tags:
      - Auth
      summary: Get a role
      description: Returns a single role by its `roleId` within the given app context.
        Requires the `profiles:r` scope.
      operationId: getRole
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: roleId
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The requested role.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoleResponse"
        "404":
          description: No role exists with this roleId in the given context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    put:
      tags:
      - Auth
      summary: Update a role
      description: "Updates a role. This is a partial update: any field you omit (or\
        \ send as null) keeps its existing value. The `roleId` and `contextId` are\
        \ immutable. Scope changes take effect for access profiles that reference\
        \ this role within about five minutes. If you use a scoped credential, the\
        \ role's scopes may not exceed your own; a root API key (`sk_`) is exempt.\
        \ Requires the `profiles:u` scope."
      operationId: updateRole
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: roleId
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoleRequest"
        required: true
      responses:
        "200":
          description: The updated role.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoleResponse"
        "404":
          description: No role exists with this roleId in the given context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Auth
      summary: Delete a role
      description: "Deletes a role. A role that is still referenced by one or more\
        \ access profiles cannot be deleted: the request is rejected with 409. Reassign\
        \ or delete those profiles first, then retry. Requires the `profiles:d` scope."
      operationId: deleteRole
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: roleId
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: Role deleted.
        "404":
          description: No role exists with this roleId in the given context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/profiles/{principalId}/versions:

    get:
      tags:
      - Auth
      summary: Get an access profile's version history
      description: "Returns the audit trail of changes (create, update, and delete\
        \ events) for an access profile, most recent first. Version history is always\
        \ recorded for every access profile; there is no setting to turn it off. Results\
        \ are paginated. Requires the `profiles:r` scope."
      operationId: getAccessProfileVersions
      parameters:
      - name: contextId
        in: path
        required: true
        schema:
          type: string
      - name: principalId
        in: path
        required: true
        schema:
          type: string
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value returned by the
          previous page to fetch the next page; omit it to start from the most recent
          versions.
        schema:
          type: string
      responses:
        "200":
          description: A page of version-history entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No access profile exists with this principalId in the given
            context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/app-contexts/{contextId}/roles/{roleId}/versions:

    get:
      tags:
      - Auth
      summary: Get a role's version history
      description: "Returns the audit trail of changes (create, update, and delete\
        \ events) for a role, newest first. Version history is always recorded for\
        \ every role; there is no setting to turn it off. Results are paginated. Requires\
        \ the `profiles:r` scope."
      operationId: getRoleVersions
      parameters:
      - name: contextId
        in: path
        description: Your identifier for the app context.
        required: true
        schema:
          type: string
        example: myapp
      - name: roleId
        in: path
        required: true
        schema:
          type: string
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value returned by the
          previous page to fetch the next page; omit it to start from the most recent
          versions.
        schema:
          type: string
      responses:
        "200":
          description: A page of version-history entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No role exists with this roleId in the given context.
        "403":
          description: Not authorized.
      security:
        - BearerAuth: []
  /v1/usage:

    get:
      tags:
      - Auth
      summary: Get usage for a calendar month
      description: "Returns full usage detail for the requested calendar month, broken\
        \ down by category (search, documents, and records) with per-category credit\
        \ estimates and a split between your live and test environments. Defaults\
        \ to the current month when `year` and `month` are omitted. Requires the `billing:r`\
        \ scope on scoped tokens; API keys always have access."
      operationId: getUsage
      parameters:
      - name: year
        in: query
        description: "Calendar year (for example, 2026). Defaults to the current year."
        schema:
          type: integer
          format: int32
        example: 2026
      - name: month
        in: query
        description: "Calendar month, 1-12 (for example, 5 for May). Defaults to the\
          \ current month."
        schema:
          type: integer
          format: int32
        example: 5
      - name: contextId
        in: query
        description: "App context id. When supplied, the `contexts` breakdown is restricted\
          \ to that single app context; your account-wide totals are unaffected."
        schema:
          type: string
        example: default
      responses:
        "200":
          description: Usage detail for the requested period.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageReportResponse"
        "400":
          description: The supplied `year` or `month` value is non-numeric or out
            of range.
        "403":
          description: Your token lacks the `billing:r` scope.
      security:
        - BearerAuth: []
  /v1/clients:

    get:
      tags:
      - Identity
      summary: List clients
      description: "Returns a paginated list of clients in your account. Narrow the\
        \ results with `orgId` or `userId`, or use `externalId` for an exact lookup\
        \ by your own identifier. For schema-bound clients, you can also query by\
        \ a schema-declared lookup field using `type`, `field`, and one lookup mode\
        \ (`value` for equality, `from`/`to` for a range, or `prefix`). The response\
        \ is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom`\
        \ to fetch the next page. Requires the `clients:r` scope."
      operationId: listClients
      parameters:
      - name: orgId
        in: query
        description: Return only clients belonging to this organization. Pass the
          Vectros-assigned UUID of an org; use `GET /v1/orgs?externalId=` to resolve
          your own identifier to this UUID.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: userId
        in: query
        description: Return only clients owned by this user. Pass the Vectros-assigned
          UUID of a user; use `GET /v1/users?externalId=` to resolve your own identifier
          to this UUID.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: externalId
        in: query
        description: "Look up a client by your own `externalId`. Returns a single-element\
          \ list, or an empty list if no client matches."
        schema:
          type: string
        example: patient_789
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: limit
        in: query
        description: Maximum number of clients to return per page (1–100; defaults
          to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: type
        in: query
        description: Record type of the schema whose lookup fields you are querying.
          Must be supplied together with `field` and exactly one lookup mode.
        schema:
          type: string
        example: client_v1
      - name: field
        in: query
        description: Name of the lookup field to filter by. Must be declared as a
          lookup field on the schema identified by `type`. Supply it together with
          `type` and exactly one lookup mode.
        schema:
          type: string
        example: industry
      - name: value
        in: query
        description: Exact value to match for `field` (equality mode). Mutually exclusive
          with `from`/`to` and `prefix`. Not allowed for a sensitive field — use `POST
          /v1/clients/lookup` instead so the value is not exposed in the URL.
        schema:
          type: string
        example: healthcare
      - name: from
        in: query
        description: "Inclusive lower bound for a range lookup. Requires `to`, and\
          \ is only allowed on non-sensitive fields that have range queries enabled.\
          \ Mutually exclusive with `value` and `prefix`."
        schema:
          type: string
        example: 2024-01-01
      - name: to
        in: query
        description: Inclusive upper bound for a range lookup. Requires `from`.
        schema:
          type: string
        example: 2024-12-31
      - name: prefix
        in: query
        description: Match clients whose value for `field` starts with this prefix.
          Only allowed on string fields that have range queries enabled. Mutually
          exclusive with `value` and `from`/`to`.
        schema:
          type: string
        example: health
      - name: order
        in: query
        description: "Sort direction for the results: `asc` (the default) or `desc`."
        schema:
          type: string
          default: asc
          enum:
          - asc
          - desc
        example: asc
      responses:
        "200":
          description: "A page of clients as a `{data, nextCursor}` envelope; `nextCursor`\
            \ is null when no more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Identity
      summary: Create or retrieve a client (idempotent by externalId)
      description: "Creates a new client identity in your account. This call is idempotent\
        \ on `externalId`: if a client with the same `externalId` already exists,\
        \ the existing record is returned instead of creating a duplicate. Requires\
        \ the `clients:c` scope."
      operationId: createClient
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientRequest"
        required: true
      responses:
        "201":
          description: "The client was created, or the existing client with the same\
            \ `externalId` was returned."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientResponse"
        "400":
          description: "The request body failed validation (for example, a missing\
            \ or malformed `externalId`)."
      security:
        - BearerAuth: []
  /v1/clients/{id}:

    get:
      tags:
      - Identity
      summary: Get a client by ID
      description: Returns a single client by its Vectros-assigned UUID. Requires
        the `clients:r` scope.
      operationId: getClient
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the client to retrieve.
        required: true
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      responses:
        "200":
          description: The requested client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientResponse"
        "404":
          description: No client with the given ID exists in your account.
      security:
        - BearerAuth: []
    put:
      tags:
      - Identity
      summary: Update a client
      description: Updates mutable fields on an existing client. Omitted fields are
        preserved (a null does not clear a field); when `payload` is supplied it replaces
        the stored payload in full rather than being deep-merged. Requires the `clients:u`
        scope.
      operationId: updateClient
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientRequest"
        required: true
      responses:
        "200":
          description: The updated client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientResponse"
        "404":
          description: No client with the given ID exists in your account.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Identity
      summary: Delete a client
      description: Permanently deletes the client. This action cannot be undone. Requires
        the `clients:d` scope.
      operationId: deleteClient
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The client was deleted. No response body is returned.
        "404":
          description: No client with the given ID exists in your account.
      security:
        - BearerAuth: []
  /v1/clients/lookup:

    post:
      tags:
      - Identity
      summary: Look up clients by field value via a request body (safe for sensitive
        fields)
      description: "Body-based equivalent of the `type`/`field`/`value` lookup on\
        \ `GET /v1/clients`. Use this when looking up by a sensitive (blind-indexed)\
        \ field: the value travels in the request body rather than the URL. The `GET`\
        \ list rejects a sensitive field's value and directs you here. The response\
        \ is a `{data, nextCursor}` envelope. Requires the `clients:r` scope."
      operationId: lookupClients
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IdentityLookupRequest"
        required: true
      responses:
        "200":
          description: "A page of matching clients as a `{data, nextCursor}` envelope;\
            \ `nextCursor` is null when no more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientPage"
        "400":
          description: "The request body was missing or failed validation (for example,\
            \ a missing `type` or `field`, or more than one lookup mode supplied)."
      security:
        - BearerAuth: []
  /v1/clients/{id}/versions:

    get:
      tags:
      - Identity
      summary: Get a client's version history
      description: "Returns the audit trail of changes to a client, newest first.\
        \ Identity auditing is always on, so this history is always available; sensitive\
        \ field values are redacted in each version. The response is a `{data, nextCursor}`\
        \ envelope. Requires the `clients:r` scope."
      operationId: getClientVersions
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the client whose history you want.
        required: true
        schema:
          type: string
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
      responses:
        "200":
          description: "A page of version-history entries as a `{data, nextCursor}`\
            \ envelope; `nextCursor` is null when no more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No client with the given ID exists in your account.
      security:
        - BearerAuth: []
  /v1/documents:

    get:
      tags:
      - Documents
      summary: List documents
      description: "Returns a paginated list of your documents, optionally filtered\
        \ by folder (`folderId`) and/or owner (`userId`, `orgId`, or `clientId`).\
        \ The response is a `{data, nextCursor}` envelope; pass `nextCursor` back\
        \ as `startFrom` to fetch the next page. Requires the `documents:r` scope."
      operationId: listDocuments
      parameters:
      - name: userId
        in: query
        description: "Filter by owning user — the Vectros-assigned UUID of a user.\
          \ To resolve from your own identifier, call GET /v1/users?externalId=."
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: orgId
        in: query
        description: "Filter by owning organization — the Vectros-assigned UUID of\
          \ an organization. To resolve from your own identifier, call GET /v1/orgs?externalId=."
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: clientId
        in: query
        description: "Filter by associated client — the Vectros-assigned UUID of a\
          \ client. To resolve from your own identifier, call GET /v1/clients?externalId=."
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: folderId
        in: query
        description: List only documents in this folder (the Vectros folder ID). Can
          be combined with the owner filters.
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: startFrom
        in: query
        description: Pagination cursor — pass the `nextCursor` returned by the previous
          page.
        schema:
          type: string
        example: doc_prev123
      - name: limit
        in: query
        description: Maximum number of documents to return per page (1-100; defaults
          to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: "A page of documents as a `{data, nextCursor}` envelope; `nextCursor`\
            \ is null when there are no more pages."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Documents
      summary: Ingest a text document
      description: Creates a document from a raw text string and queues it for asynchronous
        indexing so it becomes searchable. Requires the `documents:c` scope.
      operationId: ingestDocument
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocumentRequest"
        required: true
      responses:
        "201":
          description: Document created and queued for indexing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
        "400":
          description: "The request was invalid (for example, `text` is missing, or\
            \ the payload failed schema validation)."
      security:
        - BearerAuth: []
  /v1/documents/{id}:

    get:
      tags:
      - Documents
      summary: Get a document by ID
      description: "Returns a single document by its ID, including its full structured\
        \ payload. Requires the `documents:r` scope."
      operationId: getDocument
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The requested document.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
        "404":
          description: "No document with this ID exists in your account, or it is\
            \ outside your access scope."
      security:
        - BearerAuth: []
    put:
      tags:
      - Documents
      summary: Update a text document
      description: "Replaces the mutable fields of a document. This is a full replacement\
        \ of the payload — to merge fields instead, use PATCH. If you supply new `text`,\
        \ the document body is re-ingested and re-queued for indexing. Requires the\
        \ `documents:u` scope."
      operationId: updateDocument
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocumentRequest"
        required: true
      responses:
        "200":
          description: The updated document.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
        "400":
          description: "The request was invalid (for example, the payload failed schema\
            \ validation)."
        "404":
          description: "No document with this ID exists in your account, or it is\
            \ outside your access scope."
        "409":
          description: "Version conflict (errorCode `VERSION_CONFLICT`): the document\
            \ was modified since the `expectedVersion` you supplied (optimistic concurrency).\
            \ Re-read the document and retry. Only returned when the request includes\
            \ `expectedVersion`."
      security:
        - BearerAuth: []
    delete:
      tags:
      - Documents
      summary: Delete a document
      description: Permanently deletes the document and removes it from the search
        index. This cannot be undone. Requires the `documents:d` scope.
      operationId: deleteDocument
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The document was deleted.
        "404":
          description: "No document with this ID exists in your account, or it is\
            \ outside your access scope."
      security:
        - BearerAuth: []
    patch:
      tags:
      - Documents
      summary: Partially update a document (RFC 7386 JSON Merge Patch)
      description: "Partially updates a document using an RFC 7386 JSON Merge Patch.\
        \ The `payload` object is deep-merged: keys you send overwrite existing values\
        \ (recursing into nested objects), a key set to `null` is deleted, and keys\
        \ you omit are preserved — unlike PUT, which replaces the whole payload. Top-level\
        \ fields (`title`, `storeText`, `folderId`, `schemaId`, ownership) are set\
        \ when present and left unchanged when omitted; sending a top-level field\
        \ as `null` is rejected. Supplying `text` re-ingests the document body (same\
        \ as PUT). `indexMode` and `externalId` are immutable and rejected if present.\
        \ The merged result is validated against the bound schema. Pass `expectedVersion`\
        \ for optimistic concurrency (409 on conflict). Requires the `documents:u`\
        \ scope."
      operationId: patchDocument
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocumentRequest"
        required: true
      responses:
        "200":
          description: The updated document.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
        "400":
          description: "The request was invalid: an immutable field was sent, a top-level\
            \ field was set to `null`, or the merged result failed schema validation."
        "404":
          description: "No document with this ID exists in your account, or it is\
            \ outside your access scope."
        "409":
          description: "Version conflict (errorCode `VERSION_CONFLICT`): the document\
            \ was modified since the `expectedVersion` you supplied. Re-read the document\
            \ and retry. Only returned when the request includes `expectedVersion`."
      security:
        - BearerAuth: []
  /v1/documents/lookup:

    get:
      tags:
      - Documents
      summary: Look up documents by field value
      description: "Finds documents of a given type by a schema-declared lookup field.\
        \ The document must be bound to a schema (via `schemaId`) that declares the\
        \ field as a lookup field. A lookup on a sensitive field is rejected here,\
        \ because the value would appear in the URL query string; use POST /v1/documents/lookup\
        \ (the request-body variant) for a sensitive field instead. Results are paginated:\
        \ set `limit` for the page size and feed the returned `nextCursor` back as\
        \ `startFrom` to fetch the next page. The response is a `{data, nextCursor}`\
        \ envelope. Requires the `documents:r` scope."
      operationId: lookupDocuments
      parameters:
      - name: type
        in: query
        description: The document type to look up (the `type` of the bound schema).
        required: true
        schema:
          type: string
        example: invoice
      - name: field
        in: query
        description: The name of the lookup field declared on the schema.
        required: true
        schema:
          type: string
        example: po_number
      - name: value
        in: query
        description: Exact value to match. Mutually exclusive with `from`/`to` and
          `prefix`. Rejected for a sensitive field — use POST /v1/documents/lookup
          so the value isn't exposed in the URL.
        schema:
          type: string
        example: PO-1001
      - name: from
        in: query
        description: "Inclusive lower bound for a range lookup (requires `to`; range-enabled,\
          \ non-sensitive fields only). Mutually exclusive with `value` and `prefix`."
        schema:
          type: string
        example: 2024-01-01
      - name: to
        in: query
        description: Inclusive upper bound for a range lookup (requires `from`).
        schema:
          type: string
        example: 2024-12-31
      - name: prefix
        in: query
        description: Prefix to match for a prefix lookup (range-enabled string fields
          only). Mutually exclusive with `value` and `from`/`to`.
        schema:
          type: string
        example: PO-2024
      - name: startFrom
        in: query
        description: Pagination cursor — pass the `nextCursor` returned by the previous
          page.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: limit
        in: query
        description: Maximum number of documents to return per page (1-100; defaults
          to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: order
        in: query
        description: "Sort direction for the returned documents: `asc` (the default)\
          \ or `desc`."
        schema:
          type: string
          default: asc
          enum:
          - asc
          - desc
        example: asc
      responses:
        "200":
          description: "A page of matching documents as a `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentLookupPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Documents
      summary: Look up documents by field value via a request body (safe for sensitive
        fields)
      description: "Request-body equivalent of GET /v1/documents/lookup. Use this\
        \ when looking up by a sensitive field: the value travels in the request body\
        \ (and is blind-indexed server-side) instead of the URL query string, so it\
        \ never lands in access, CDN, or proxy logs. The GET variant rejects `value`\
        \ for a sensitive field and directs you here. Requires the `documents:r` scope."
      operationId: lookupDocumentsByBody
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocumentLookupRequest"
        required: true
      responses:
        "200":
          description: "A page of matching documents as a `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentLookupPage"
        "400":
          description: "The request was invalid (for example, a missing `type` or\
            \ `field`, or conflicting lookup modes)."
      security:
        - BearerAuth: []
  /v1/documents/{id}/download:

    get:
      tags:
      - Documents
      summary: Get a presigned download URL for a file-backed document
      description: Returns a short-lived presigned S3 GET URL for the original uploaded
        file. Only available for file-backed documents (created via POST /v1/documents/upload);
        text-only documents return 400. Requires the `documents:r` scope.
      operationId: getDocumentDownloadUrl
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: A presigned download URL for the file.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentDownloadResponse"
        "400":
          description: "The document is text-only, so there is no file to download."
        "404":
          description: "The document or its underlying file does not exist, or it\
            \ is outside your access scope."
      security:
        - BearerAuth: []
  /v1/documents/{id}/text:

    get:
      tags:
      - Documents
      summary: Retrieve a document's stored text
      description: "Returns the full extracted or ingested text body for documents\
        \ that were stored with `storeText=true`. Returns 404 when the document does\
        \ not exist or when no text is available (because `storeText` was false, or\
        \ extraction has not yet completed). Requires the `documents:r` scope."
      operationId: getDocumentText
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The document's text body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentTextResponse"
        "404":
          description: "The document does not exist, is outside your access scope,\
            \ or has no stored text available."
      security:
        - BearerAuth: []
  /v1/documents/{id}/versions:

    get:
      tags:
      - Documents
      summary: Get version history for a document
      description: "Returns the audit trail of changes (CREATE, UPDATE, DELETE) for\
        \ a document. History is recorded only for documents bound to a schema that\
        \ has audit history enabled (the default for typed documents); untyped documents\
        \ have no version history. The response is a `{data, nextCursor}` envelope.\
        \ Requires the `documents:r` scope."
      operationId: getDocumentVersions
      parameters:
      - name: id
        in: path
        description: The ID of the document whose history you want.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: startFrom
        in: query
        description: Pagination cursor — pass the `nextCursor` returned by the previous
          page.
        schema:
          type: string
      responses:
        "200":
          description: "The document's version history as a `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: "No document with this ID exists in your account, or it is\
            \ outside your access scope."
      security:
        - BearerAuth: []
  /v1/documents/upload:

    post:
      tags:
      - Documents
      summary: Initiate a file upload
      description: Starts a file-based document by returning a short-lived presigned
        S3 PUT URL. Upload the file bytes directly to `uploadUrl`; the document is
        then automatically queued for text extraction and asynchronous indexing. Requires
        the `documents:c` scope.
      operationId: uploadDocument
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FileUploadRequest"
        required: true
      responses:
        "201":
          description: A presigned upload URL was generated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileUploadResponse"
        "400":
          description: "The request was invalid (for example, `fileName` or `fileType`\
            \ is missing)."
      security:
        - BearerAuth: []
  /v1/erasure-requests:

    post:
      tags:
      - Compliance
      summary: Submit an end-subject erasure request
      description: "Submits a right-to-erasure request for a single end-subject (a\
        \ user, client, or organization). Erasure removes exactly the data the subject\
        \ solely owns across the declared contexts, plus the subject's identity and\
        \ lookup rows. It never touches another account's data and never cascades\
        \ into another subject's data. The request is asynchronous: it returns 202\
        \ with a `requestId`; poll `GET /v1/erasure-requests/{id}` until the job completes\
        \ to obtain the completion certificate. Requires a root API key — a scoped\
        \ credential is rejected with 403."
      operationId: createErasureRequest
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ErasureRequest"
        required: true
      responses:
        "202":
          description: Erasure request accepted. The body carries the request id and
            initial status; poll the GET endpoint for the certificate.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErasureRequestResponse"
        "403":
          description: Not authorized. This operation requires a root API key; scoped
            credentials cannot submit erasure requests.
        "501":
          description: This endpoint is not yet implemented.
      security:
        - BearerAuth: []
  /v1/erasure-requests/{id}:

    get:
      tags:
      - Compliance
      summary: Get the status and certificate of an erasure request
      description: "Polls an erasure request by id. While the job is still running\
        \ this returns its status only; once it completes, the response also includes\
        \ the verifiable completion certificate (which contexts were swept, per-context\
        \ deletion counts, and reports of dangling references and shared rows that\
        \ were left intact). Requires a root API key."
      operationId: getErasureRequest
      parameters:
      - name: id
        in: path
        description: "The id of the erasure request to poll, as returned when the\
          \ request was submitted."
        required: true
        schema:
          type: string
        example: er_550e8400e29b41d4a716446655440000
      responses:
        "200":
          description: "The current status of the erasure request, plus the completion\
            \ certificate once it has finished."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErasureRequestResponse"
        "403":
          description: Not authorized. This operation requires a root API key; scoped
            credentials cannot read erasure requests.
        "404":
          description: No erasure request exists with the given id.
        "501":
          description: This endpoint is not yet implemented.
      security:
        - BearerAuth: []
  /v1/admin/export:

    post:
      tags:
      - Compliance
      summary: Submit a data-export job
      description: "Submits an asynchronous job to export your account's data, or\
        \ a single end-subject's data, across the requested contexts. The request\
        \ returns 202 with an `exportJobId`; poll `GET /v1/admin/export/{id}` until\
        \ the job completes to obtain a short-lived presigned download URL and a manifest\
        \ describing the payload. Requires a root API key — a scoped credential is\
        \ rejected with 403."
      operationId: createExport
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExportRequest"
        required: true
      responses:
        "202":
          description: Export job accepted. The body carries the job id and initial
            status; poll the GET endpoint for the result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExportRequestResponse"
        "403":
          description: Not authorized. This operation requires a root API key; scoped
            credentials cannot submit export jobs.
        "501":
          description: This endpoint is not yet implemented.
      security:
        - BearerAuth: []
  /v1/admin/export/{id}:

    get:
      tags:
      - Compliance
      summary: Get the status and result of an export job
      description: "Polls an export job by id. While the job is still running this\
        \ returns its status only; once it completes, the response also includes a\
        \ short-lived presigned download URL and a manifest (format version and per-context\
        \ counts). Requires a root API key."
      operationId: getExport
      parameters:
      - name: id
        in: path
        description: "The id of the export job to poll, as returned when the job was\
          \ submitted."
        required: true
        schema:
          type: string
        example: exp_550e8400e29b41d4a716446655440000
      responses:
        "200":
          description: "The current status of the export job, plus the download URL\
            \ and manifest once it has completed."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExportRequestResponse"
        "403":
          description: Not authorized. This operation requires a root API key; scoped
            credentials cannot read export jobs.
        "404":
          description: No export job exists with the given id.
        "501":
          description: This endpoint is not yet implemented.
      security:
        - BearerAuth: []
  /v1/folders:

    get:
      tags:
      - Folders
      summary: List folders
      description: "Returns a paginated list of your folders. Pass `parentFolderId`\
        \ to list the direct children of a specific folder (tree navigation); omit\
        \ it for a flat list across your account. You can also filter by owner using\
        \ `userId`, `orgId`, or `clientId`. Results are returned as a `{data, nextCursor}`\
        \ envelope — pass `nextCursor` as `startFrom` to fetch the next page. Requires\
        \ the `folders:r` scope."
      operationId: listFolders
      parameters:
      - name: parentFolderId
        in: query
        description: List only the direct children of this folder — the Vectros-assigned
          UUID of a folder — for tree navigation. Omit for a flat list across your
          account.
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: orgId
        in: query
        description: Filter to folders owned by this organization — the Vectros-assigned
          UUID of an organization. Use `GET /v1/orgs?externalId=` to resolve your
          own external ID to this UUID.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: userId
        in: query
        description: Filter to folders owned by this user — the Vectros-assigned UUID
          of a user. Use `GET /v1/users?externalId=` to resolve your own external
          ID to this UUID.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: clientId
        in: query
        description: Filter to folders associated with this client — the Vectros-assigned
          UUID of a client. Use `GET /v1/clients?externalId=` to resolve your own
          external ID to this UUID.
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value from the previous
          page to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: fld_prev123
      - name: limit
        in: query
        description: Maximum number of folders to return per page. Must be between
          1 and 100; defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: "A page of folders, returned as a `{data, nextCursor}` envelope.\
            \ `nextCursor` is null when there are no more pages."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Folders
      summary: Create a folder
      description: "Creates a folder to organize your documents and records. If `parentFolderId`\
        \ is omitted, the folder is created under your context's default root folder.\
        \ Requires the `folders:c` scope."
      operationId: createFolder
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FolderRequest"
        required: true
      responses:
        "201":
          description: "The folder was created (or, if a folder with the same slug\
            \ already exists under the same parent, that existing folder is returned)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderResponse"
        "400":
          description: "The request was invalid — for example, the folder name was\
            \ missing or the supplied slug was malformed."
      security:
        - BearerAuth: []
  /v1/folders/{id}:

    get:
      tags:
      - Folders
      summary: Get a folder by ID
      description: Retrieves a single folder by its ID. Requires the `folders:r` scope.
      operationId: getFolder
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The folder was found and returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderResponse"
        "404":
          description: No folder with the given ID exists in your account.
      security:
        - BearerAuth: []
    put:
      tags:
      - Folders
      summary: Update a folder
      description: Replaces a folder's name and description. Omitted fields are preserved
        (a null does not clear a field). The slug and parent folder are immutable
        and cannot be changed here. Requires the `folders:u` scope.
      operationId: updateFolder
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FolderRequest"
        required: true
      responses:
        "200":
          description: The folder was updated and the new representation is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderResponse"
        "404":
          description: No folder with the given ID exists in your account.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Folders
      summary: Delete a folder
      description: Permanently deletes a folder. The folder must be empty (contain
        no documents or sub-folders) and must not be protected. Your context's root
        folder is protected and cannot be deleted. Requires the `folders:d` scope.
      operationId: deleteFolder
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The folder was deleted. No response body is returned.
        "400":
          description: The folder cannot be deleted because it still contains items
            or is protected.
        "404":
          description: No folder with the given ID exists in your account.
      security:
        - BearerAuth: []
    patch:
      tags:
      - Folders
      summary: Partially update a folder (RFC 7386 JSON Merge Patch)
      description: "Partially updates a folder using an RFC 7386 JSON Merge Patch.\
        \ The `name`, `description`, and ownership fields (`userId`, `orgId`, `clientId`)\
        \ are applied when present and left unchanged when omitted; sending any of\
        \ these as null is rejected, because clearing a field is not supported in\
        \ this release (omit it instead). `slug` and `parentFolderId` are immutable\
        \ — a folder cannot be re-slugged or moved via the API — and the request is\
        \ rejected if either is present. Pass `expectedVersion` for optimistic concurrency\
        \ (you get a 409 if the folder changed since you last read it). Requires the\
        \ `folders:u` scope."
      operationId: patchFolder
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FolderRequest"
        required: true
      responses:
        "200":
          description: The folder was updated and the new representation is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderResponse"
        "400":
          description: "The patch was invalid — it attempted to change an immutable\
            \ field (`slug` or `parentFolderId`) or set `name`, `description`, or\
            \ an ownership field (`userId`, `orgId`, `clientId`) to null."
        "404":
          description: No folder with the given ID exists in your account.
        "409":
          description: Version conflict — the folder was modified since the `expectedVersion`
            you supplied. Re-read the folder and retry. Returned only when the request
            includes `expectedVersion`.
      security:
        - BearerAuth: []
  /v1/folders/{id}/versions:

    get:
      tags:
      - Folders
      summary: Get version history for a folder
      description: "Returns the audit trail of changes (create, update, and delete\
        \ events) for a folder, newest first. Folder version history is always recorded;\
        \ there is no setting to turn it off. Results are returned as a `{data, nextCursor}`\
        \ envelope — pass `nextCursor` as `startFrom` to fetch the next page. Requires\
        \ the `folders:r` scope."
      operationId: getFolderVersions
      parameters:
      - name: id
        in: path
        description: The ID of the folder whose history you want.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value from the previous
          page to fetch the next page; omit it for the first page.
        schema:
          type: string
      responses:
        "200":
          description: "A page of version-history entries, returned as a `{data, nextCursor}`\
            \ envelope. `nextCursor` is null when there are no more pages."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No folder with the given ID exists in your account.
      security:
        - BearerAuth: []
  /v1/models:

    get:
      tags:
      - Inference
      summary: List available inference models
      description: "Returns every inference model available to you, including each\
        \ model's context window, the plan tiers it's available on, and the exact\
        \ credit rates charged per 1K input and output tokens. Use this to populate\
        \ model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`,\
        \ or `/v1/documents/{id}/ask`."
      operationId: listInferenceModels
      responses:
        "200":
          description: "The catalog of available inference models, with the default\
            \ model alias."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelsResponse"
      security:
        - BearerAuth: []
  /v1/orgs:

    get:
      tags:
      - Identity
      summary: List organizations
      description: "Returns a paginated list of organizations in your account. Filter\
        \ by `userId` to return only the organizations owned by a specific user, or\
        \ by `externalId` for an exact lookup using your own identifier. You can also\
        \ query schema-declared lookup fields by supplying `type` and `field` together\
        \ with one lookup mode (`value` for equality, `from`/`to` for a range, or\
        \ `prefix`). Requires the `orgs:r` scope."
      operationId: listOrgs
      parameters:
      - name: userId
        in: query
        description: "Return only organizations owned by this user, given as the Vectros-assigned\
          \ ID (UUID) of a user. To resolve a user ID from your own identifier, call\
          \ `GET /v1/users?externalId=`."
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: externalId
        in: query
        description: "Look up an organization by your own identifier. Returns a list\
          \ with the single matching organization, or an empty list if none matches."
        schema:
          type: string
        example: clinic_001
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: limit
        in: query
        description: Maximum number of organizations to return per page (1–100; defaults
          to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: type
        in: query
        description: "The schema record type whose lookup fields you want to query.\
          \ Must be supplied together with `field` and exactly one lookup mode (`value`,\
          \ `from`/`to`, or `prefix`)."
        schema:
          type: string
        example: org_v1
      - name: field
        in: query
        description: The schema-declared lookup field to filter on. Must be one of
          the lookup fields defined on the schema named by `type`. Supplied together
          with `type` and one lookup mode.
        schema:
          type: string
        example: region
      - name: value
        in: query
        description: Exact value to match for `field` (equality lookup). Mutually
          exclusive with `from`/`to` and `prefix`. Not allowed for a sensitive field
          — use `POST /v1/orgs/lookup` instead so the value is not exposed in the
          URL.
        schema:
          type: string
        example: us-east
      - name: from
        in: query
        description: "Inclusive lower bound for a range lookup. Requires `to`, and\
          \ is only supported on non-sensitive fields that have range lookups enabled.\
          \ Mutually exclusive with `value` and `prefix`."
        schema:
          type: string
        example: 2024-01-01
      - name: to
        in: query
        description: Inclusive upper bound for a range lookup. Requires `from`.
        schema:
          type: string
        example: 2024-12-31
      - name: prefix
        in: query
        description: Match all values for `field` that start with this prefix. Only
          supported on string fields that have range lookups enabled. Mutually exclusive
          with `value` and `from`/`to`.
        schema:
          type: string
        example: us-
      - name: order
        in: query
        description: "Sort direction for lookup results: `asc` (ascending, the default)\
          \ or `desc` (descending)."
        schema:
          type: string
          default: asc
          enum:
          - asc
          - desc
        example: asc
      responses:
        "200":
          description: "A page of organizations as a `{data, nextCursor}` envelope.\
            \ `nextCursor` is an opaque cursor for the next page, or null when no\
            \ more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Identity
      summary: Create an organization (idempotent by external ID)
      description: "Creates a new organization in your account. This call is idempotent\
        \ on `externalId`: if an organization with the same `externalId` already exists,\
        \ the existing record is returned instead of creating a duplicate. Requires\
        \ the `orgs:c` scope."
      operationId: createOrg
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgRequest"
        required: true
      responses:
        "201":
          description: "The organization that was created, or the existing organization\
            \ if one already matched the supplied `externalId`."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "400":
          description: The request was invalid — for example a missing required field
            or a malformed value.
      security:
        - BearerAuth: []
  /v1/orgs/{id}:

    get:
      tags:
      - Identity
      summary: Get an organization by ID
      description: "Retrieves a single organization by its Vectros-assigned ID, returning\
        \ its current name, status, payload, and schema binding. Requires the `orgs:r`\
        \ scope."
      operationId: getOrg
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned ID (UUID) of the organization to retrieve.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      responses:
        "200":
          description: The requested organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "404":
          description: No organization with this ID exists in your account.
      security:
        - BearerAuth: []
    put:
      tags:
      - Identity
      summary: Update an organization
      description: "Updates the mutable fields of an organization. Omitted fields\
        \ are preserved (a null value does not clear a field), and the `payload` object\
        \ is replaced in full when supplied rather than deep-merged. Requires the\
        \ `orgs:u` scope."
      operationId: updateOrg
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgRequest"
        required: true
      responses:
        "200":
          description: The updated organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "404":
          description: No organization with this ID exists in your account.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Identity
      summary: Delete an organization
      description: Permanently deletes an organization. This action cannot be undone.
        Requires the `orgs:d` scope.
      operationId: deleteOrg
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The organization was deleted. No content is returned.
        "404":
          description: No organization with this ID exists in your account.
      security:
        - BearerAuth: []
  /v1/orgs/lookup:

    post:
      tags:
      - Identity
      summary: Look up organizations by field value (request-body variant)
      description: "Looks up organizations by a schema-declared field value, with\
        \ the search criteria sent in the request body instead of the URL. Use this\
        \ when looking up by a sensitive field: the value travels in the body and\
        \ is never exposed in the URL. This is the body-based equivalent of the `type`/`field`/`value`\
        \ lookup on `GET /v1/orgs`, which rejects sensitive-field values and directs\
        \ you here. Returns a `{data, nextCursor}` envelope. Requires the `orgs:r`\
        \ scope."
      operationId: lookupOrgs
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IdentityLookupRequest"
        required: true
      responses:
        "200":
          description: "A page of matching organizations as a `{data, nextCursor}`\
            \ envelope. `nextCursor` is an opaque cursor for the next page, or null\
            \ when no more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgPage"
        "400":
          description: "The lookup request was invalid — for example a missing `type`\
            \ or `field`, or more than one lookup mode supplied."
      security:
        - BearerAuth: []
  /v1/orgs/{id}/versions:

    get:
      tags:
      - Identity
      summary: Get an organization's version history
      description: "Returns the audit trail of changes made to an organization, newest\
        \ first. Version history is always recorded for identity entities, and sensitive\
        \ field values are redacted in the history. Requires the `orgs:r` scope."
      operationId: getOrgVersions
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned ID (UUID) of the organization whose history
          you want.
        required: true
        schema:
          type: string
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` value from the previous
          page to fetch the next page of history.
        schema:
          type: string
      responses:
        "200":
          description: "A page of historical versions as a `{data, nextCursor}` envelope.\
            \ `nextCursor` is an opaque cursor for the next page, or null when no\
            \ more pages remain."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No organization with this ID exists in your account.
      security:
        - BearerAuth: []
  /v1/ping:

    get:
      tags:
      - Auth
      summary: Verify your credential and return its bound identity
      description: "Returns the identity bound to your credential — your account,\
        \ principal type, key id, and scope details — so you can confirm who you are\
        \ authenticated as and that the credential is valid. MCP clients use this\
        \ to render \"signed in as ...\" in a chat UI without a separate identity\
        \ endpoint."
      operationId: ping
      responses:
        "200":
          description: Authentication succeeded; the response carries your credential's
            identity binding.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PingResponse"
      security:
        - BearerAuth: []
  /v1/principals/{principalId}/profiles:

    get:
      tags:
      - Auth
      summary: List all access profiles for a principal across every context
      description: "Returns the access profiles for the given principal across all\
        \ of your contexts. Use this to answer questions like \"which apps does this\
        \ user have access to?\" — for example, to build a member-access summary.\
        \ Results are confined to your account. Requires the `profiles:r` scope."
      operationId: listProfilesForPrincipal
      parameters:
      - name: principalId
        in: path
        description: Identifier of the principal to look up — a user (`usr_<userId>`)
          or an API key (`key_<keyId>`).
        required: true
        schema:
          type: string
        example: usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of access profiles to return per page (1–100;
          defaults to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: A page of the principal's access profiles across your contexts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessProfilePage"
        "400":
          description: The `principalId` is malformed.
        "403":
          description: Your token lacks the `profiles:r` scope.
      security:
        - BearerAuth: []
  /v1/records/batch-get:

    post:
      tags:
      - Records
      summary: Batch get records by ID (reserved — not yet implemented)
      description: "Reserved endpoint for fetching multiple records by ID in one call.\
        \ When available, the response will contain only the records you can see;\
        \ any IDs that do not exist or are outside your scope are silently omitted\
        \ (there is no per-ID existence signal), matching the not-found behavior of\
        \ the single-record GET. It currently returns 501 (not implemented). The documented\
        \ 200 response schema is the stable shape this endpoint will use once available.\
        \ Requires the `records:r` scope."
      operationId: batchGetRecords
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchGetRequest"
        required: true
      responses:
        "200":
          description: The subset of the requested records you can see. This is the
            reserved response shape; the endpoint does not yet return it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchGetResponse"
        "403":
          description: Your API key or token lacks the `records:r` scope required
            for this operation.
        "501":
          description: This endpoint is reserved but not yet implemented.
      security:
        - BearerAuth: []
  /v1/records/lookup/batch:

    post:
      tags:
      - Records
      summary: Batch lookup records (reserved — not yet implemented)
      description: Reserved endpoint for batch lookup and reference resolution. The
        published response shape correlates results to each input and carries a per-item
        status envelope. It currently returns 501 (not implemented). The documented
        200 response schema is the stable shape this endpoint will use once available.
        Requires the `records:r` scope.
      operationId: batchLookupRecords
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchLookupRequest"
        required: true
      responses:
        "200":
          description: Per-input lookup result groups. This is the reserved response
            shape; the endpoint does not yet return it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchLookupResponse"
        "403":
          description: Your API key or token lacks the `records:r` scope required
            for this operation.
        "501":
          description: This endpoint is reserved but not yet implemented.
      security:
        - BearerAuth: []
  /v1/records/batch:

    post:
      tags:
      - Records
      summary: Batch write records (reserved — not yet implemented)
      description: "Reserved endpoint for bulk record writes. The published response\
        \ shape includes a per-item partial-failure envelope and an atomicity flag.\
        \ It currently returns 501 (not implemented). The documented 200 response\
        \ schema is the stable shape this endpoint will use once available, published\
        \ now so SDK integrations against it will not break when it ships. Requires\
        \ the `records:c` scope."
      operationId: batchWriteRecords
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchWriteRequest"
        required: true
      responses:
        "200":
          description: Per-item write outcomes. This is the reserved response shape;
            the endpoint does not yet return it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchWriteResponse"
        "403":
          description: Your API key or token lacks the `records:c` scope required
            for this operation.
        "501":
          description: This endpoint is reserved but not yet implemented.
      security:
        - BearerAuth: []
  /v1/records:

    get:
      tags:
      - Records
      summary: List records
      description: "Returns a paginated list of records in your account as a `{data,\
        \ nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true`\
        \ to choose the mode: `type` lists all records of a single type; `folderId`\
        \ lists all records in a folder (any type); and `recent=true` returns the\
        \ account-wide recently-updated feed across all types, newest first. You may\
        \ combine `type` with `folderId` to list a single type within a folder. The\
        \ owner filters (`userId`, `orgId`, `clientId`) further narrow the type and\
        \ folder modes; the `recent` feed is standalone and ignores all filters. Each\
        \ token only sees the record types it is scoped to read. Requires the `records:r`\
        \ scope. By default the response returns the indexed projection of each record;\
        \ set `includePayload=true` to include full payloads."
      operationId: listRecords
      parameters:
      - name: type
        in: query
        description: "The record type to list, e.g. `intake_form`. Required unless\
          \ `folderId` or `recent=true` is given."
        schema:
          type: string
        example: intake_form
      - name: folderId
        in: query
        description: "List all records in this folder, regardless of type. The value\
          \ is a Vectros-assigned folder UUID. Required unless `type` or `recent=true`\
          \ is given; may be combined with `type` to list a single type within the\
          \ folder."
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: userId
        in: query
        description: Filter to records owned by this user. The value is the Vectros-assigned
          UUID of a user; resolve one from your own ID via `GET /v1/users?externalId=`.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: orgId
        in: query
        description: Filter to records owned by this organization. The value is the
          Vectros-assigned UUID of an organization; resolve one via `GET /v1/orgs?externalId=`.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: clientId
        in: query
        description: Filter to records owned by this client (requires `userId` or
          `orgId` as well). The value is the Vectros-assigned UUID of a client; resolve
          one via `GET /v1/clients?externalId=`.
        schema:
          type: string
        example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` returned by the previous
          page to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: limit
        in: query
        description: Maximum number of records to return per page. Allowed range 1–100;
          defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: includePayload
        in: query
        description: "Set to `true` to include each record's full payload in this\
          \ response, rehydrating any externally stored payloads (hydration is capped\
          \ per page). Defaults to `false`, which returns only the indexed projection;\
          \ fetch a full payload via `GET /v1/records/{id}`."
        schema:
          type: string
        example: false
      - name: recent
        in: query
        description: "Set to `true` to return the account-wide recently-updated feed\
          \ across all record types, newest first — a type-agnostic activity view.\
          \ This mode is standalone: it ignores `type`, `folderId`, and the owner\
          \ filters. Per-record scope still applies — you see only the record types\
          \ your token may read."
        schema:
          type: string
        example: true
      responses:
        "200":
          description: "A paginated page of records (a `{data, nextCursor}` envelope)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Records
      summary: Create a record
      description: "Creates a new record of a given type. The `payload` is validated\
        \ against that type's schema before the record is stored. Identify the type\
        \ by sending `typeName`, `schemaId`, or both (they must agree); if you send\
        \ only `schemaId`, the type is taken from that schema. Optionally supply an\
        \ `externalId` to make the create idempotent — if a record with the same `externalId`\
        \ already exists in your context, that existing record is returned unchanged\
        \ instead of a duplicate being created. Requires the `records:c:<type>` scope."
      operationId: createRecord
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordRequest"
            example:
              typeName: intake_form
              schemaId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
              payload:
                first_name: Jane
                email: jane@example.com
              folderId: f47ac10b-58cc-4372-a567-0e02b2c3d479
        required: true
      responses:
        "201":
          description: Record created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordResponse"
              example:
                id: 550e8400-e29b-41d4-a716-446655440000
                typeName: intake_form
                schemaId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
                payload:
                  first_name: Jane
                  email: jane@example.com
                status: ACTIVE
                createdBy: 550e8400-e29b-41d4-a716-446655440000
                createdAt: 2024-01-15T09:30:00Z
                updatedAt: 2024-01-15T09:30:00Z
        "400":
          description: "The request is invalid — for example the payload fails schema\
            \ validation, the named type has no schema, or `typeName` and `schemaId`\
            \ disagree."
        "403":
          description: Your API key or token lacks the `records:c:<type>` scope required
            to create this record type.
      security:
        - BearerAuth: []
  /v1/records/{id}:

    get:
      tags:
      - Records
      summary: Get a record by ID
      description: "Retrieves a single record by its Vectros-assigned ID, including\
        \ its full payload (payloads that were externalized to object storage are\
        \ rehydrated for this response). Sensitive fields are masked according to\
        \ the record's schema. Requires the `records:r:<type>` scope. A record outside\
        \ your account or scope returns 404 (not found) rather than revealing its\
        \ existence."
      operationId: getRecord
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the record to retrieve.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      responses:
        "200":
          description: The record was found and is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordResponse"
        "404":
          description: "No record with this ID exists in your account, or it is outside\
            \ your token's scope."
      security:
        - BearerAuth: []
    put:
      tags:
      - Records
      summary: Update a record
      description: "Replaces a record's payload and mutable fields. This is a full\
        \ replacement: the `payload` you send overwrites the existing payload entirely,\
        \ so include every field you want to keep (use the PATCH endpoint to change\
        \ only specific fields). `typeName` and `schemaId` are immutable and cannot\
        \ be changed. The new payload is validated against the record's schema. Pass\
        \ `expectedVersion` to make the update conditional on the record not having\
        \ changed since you last read it (optimistic concurrency). Requires the `records:u:<type>`\
        \ scope."
      operationId: updateRecord
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the record to update.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordRequest"
        required: true
      responses:
        "200":
          description: The record was updated and the new state is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordResponse"
        "400":
          description: The request is invalid — for example the payload fails schema
            validation.
        "404":
          description: "No record with this ID exists in your account, or it is outside\
            \ your token's scope."
        "409":
          description: "Version conflict — the record's current version no longer\
            \ matches the `expectedVersion` you supplied, so it was modified since\
            \ you last read it (optimistic concurrency). Re-read the record and retry.\
            \ Only returned when the request includes `expectedVersion`."
      security:
        - BearerAuth: []
    delete:
      tags:
      - Records
      summary: Delete a record
      description: "Permanently deletes a record. This is a hard delete: the record\
        \ is removed and a tombstone plus an audit-trail entry are recorded (you can\
        \ later retrieve the tombstone via `GET /v1/records/{id}/tombstone`). Requires\
        \ the `records:d:<type>` scope."
      operationId: deleteRecord
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the record to delete.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      responses:
        "204":
          description: The record was deleted. No content is returned.
        "404":
          description: "No record with this ID exists in your account, or it is outside\
            \ your token's scope."
      security:
        - BearerAuth: []
    patch:
      tags:
      - Records
      summary: Partially update a record (RFC 7386 JSON Merge Patch)
      description: "Partially updates a record using an RFC 7386 JSON Merge Patch.\
        \ The `payload` object is deep-merged into the existing payload: keys you\
        \ send overwrite (recursing into nested objects), a key set to null is deleted,\
        \ and keys you omit are left unchanged — so you can change a single field\
        \ without re-sending the rest (unlike the full-replacement PUT). Top-level\
        \ fields (`status`, `folderId`, `userId`, `orgId`, `clientId`) are set when\
        \ present and left unchanged when omitted; sending a top-level field as null\
        \ is rejected (clearing a top-level field is not supported in this release\
        \ — omit it instead). `typeName`, `schemaId`, `externalId`, and `indexMode`\
        \ are immutable and rejected if present. The merged result is validated against\
        \ the schema. Pass `expectedVersion` to make the patch conditional (optimistic\
        \ concurrency, 409 on conflict). Requires the `records:u:<type>` scope."
      operationId: patchRecord
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the record to patch.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordRequest"
            example:
              payload:
                status: done
              expectedVersion: 5
        required: true
      responses:
        "200":
          description: The record was updated and the merged state is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordResponse"
        "400":
          description: "The request is invalid — for example you tried to change an\
            \ immutable field, sent a top-level field as null, or the merged state\
            \ fails schema validation."
        "404":
          description: "No record with this ID exists in your account, or it is outside\
            \ your token's scope."
        "409":
          description: Version conflict — the record was modified since the `expectedVersion`
            you supplied. Re-read and retry. Only returned when the request includes
            `expectedVersion`.
      security:
        - BearerAuth: []
  /v1/records/lookup:

    get:
      tags:
      - Records
      summary: "Lookup records by field value (equality, range, or prefix)"
      description: "Finds records by the value of a lookup field declared on the type's\
        \ schema. Provide exactly one lookup mode: `value` (exact match), `from`+`to`\
        \ (inclusive range, ascending by value), or `prefix` (string fields only,\
        \ ascending). Range and prefix lookups are not supported on a sensitive field,\
        \ because its value is stored as a blind index and has no sortable order.\
        \ An exact-`value` lookup on a sensitive field is also rejected on this GET\
        \ endpoint — the value must not appear in the URL — so use the `POST /v1/records/lookup`\
        \ body variant for sensitive fields. Results are paginated: set `limit` for\
        \ the page size and pass the returned `nextCursor` back as `startFrom` for\
        \ the next page. Requires the `records:r:<type>` scope."
      operationId: lookupRecords
      parameters:
      - name: type
        in: query
        description: "The record type to look up, e.g. `intake_form`."
        required: true
        schema:
          type: string
        example: intake_form
      - name: field
        in: query
        description: "The name of the lookup field to match on, as declared on the\
          \ type's schema."
        required: true
        schema:
          type: string
        example: email
      - name: value
        in: query
        description: Exact value to match. Mutually exclusive with `from`/`to` and
          `prefix`. Rejected for a sensitive field — use `POST /v1/records/lookup`
          instead so the value is not exposed in the URL.
        schema:
          type: string
        example: jane@example.com
      - name: from
        in: query
        description: Inclusive lower bound of a range lookup (requires `to`; non-sensitive
          fields only). Mutually exclusive with `value` and `prefix`.
        schema:
          type: string
        example: 2024-01-01
      - name: to
        in: query
        description: Inclusive upper bound of a range lookup (requires `from`).
        schema:
          type: string
        example: 2024-12-31
      - name: prefix
        in: query
        description: "Prefix to match (string, non-sensitive fields only). Mutually\
          \ exclusive with `value` and `from`/`to`."
        schema:
          type: string
        example: jane
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page;
          omit it for the first page.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: limit
        in: query
        description: Maximum number of records to return per page. Allowed range 1–100;
          defaults to 20.
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: includePayload
        in: query
        description: "Set to `true` to include each record's full payload, rehydrating\
          \ any payloads stored externally (capped per page). Defaults to `false`,\
          \ which returns only the indexed projection."
        schema:
          type: string
        example: false
      - name: order
        in: query
        description: "Sort direction by the field's value: `asc` (ascending, the default)\
          \ or `desc` (descending)."
        schema:
          type: string
          default: asc
          enum:
          - asc
          - desc
        example: asc
      responses:
        "200":
          description: "A page of matching records (a `{data, nextCursor}` envelope)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordLookupResponse"
        "400":
          description: "The request is invalid — for example you supplied no lookup\
            \ mode or more than one, attempted a range or prefix lookup on a sensitive\
            \ field, or used a prefix lookup on a non-string field."
      security:
        - BearerAuth: []
    post:
      tags:
      - Records
      summary: Lookup records by field value via a request body (sensitive-value-safe)
      description: "Body-based equivalent of `GET /v1/records/lookup`. Use this when\
        \ looking up by a sensitive field: the value travels in the request body (and\
        \ is blind-indexed server-side) instead of in the URL query string, so it\
        \ never lands in access, CDN, or proxy logs. The GET variant rejects an exact-value\
        \ lookup on a sensitive field and directs you here. Non-sensitive exact-value,\
        \ range (`from`+`to`), and prefix lookups also work here. Returns the same\
        \ `{data, nextCursor}` envelope and uses the same pagination as the GET variant.\
        \ Requires the `records:r:<type>` scope."
      operationId: lookupRecordsByBody
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordLookupRequest"
        required: true
      responses:
        "200":
          description: "A page of matching records (a `{data, nextCursor}` envelope)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordLookupResponse"
        "400":
          description: "The request is invalid — for example you supplied no lookup\
            \ mode or more than one, attempted a range or prefix lookup on a sensitive\
            \ field, or used a prefix lookup on a non-string field."
      security:
        - BearerAuth: []
  /v1/records/{id}/tombstone:

    get:
      tags:
      - Records
      summary: Get the tombstone for a deleted record
      description: "Returns the tombstone left behind when a record was hard-deleted,\
        \ confirming the deletion and recording when it happened. Look it up using\
        \ the deleted record's original ID. Requires the `records:r:<type>` scope."
      operationId: getRecordTombstone
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The tombstone was found and is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecordResponse"
        "404":
          description: "No tombstone with this ID exists in your account (the record\
            \ was never deleted, never existed, or is outside your scope)."
      security:
        - BearerAuth: []
  /v1/records/{id}/versions:

    get:
      tags:
      - Records
      summary: Get a record's version history
      description: "Returns the audit trail of past versions for a record, as a paginated\
        \ `{data, nextCursor}` page. This is available only when the record type's\
        \ schema has audit history enabled (the default); if it is disabled, the endpoint\
        \ returns 409. Requires the `records:r:<type>` scope."
      operationId: getRecordVersions
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the record whose version history
          you want.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page;
          omit it for the first page.
        schema:
          type: string
      responses:
        "200":
          description: "A paginated page of past versions (a `{data, nextCursor}`\
            \ envelope)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: "No record with this ID exists in your account, or it is outside\
            \ your token's scope."
        "409":
          description: "Audit history is disabled for this record type, so no version\
            \ history is kept."
      security:
        - BearerAuth: []
  /v1/schemas:

    get:
      tags:
      - Schemas
      summary: List record schemas
      description: "Returns a paginated list of the record schemas defined in your\
        \ account. Filter by `userId` or `orgId` to scope to an owner, by `surface`\
        \ to list the types bindable to one surface, or by `recordType` to resolve\
        \ the single schema for a type directly. Filtering by an identity surface\
        \ (user, org, or client) lists your account-wide identity schemas regardless\
        \ of the calling context; filtering by record or document lists within the\
        \ calling context. Requires the `schemas:r` scope."
      operationId: listSchemas
      parameters:
      - name: userId
        in: query
        description: Filter to schemas owned by this user — the Vectros-assigned UUID
          of a user in your account. Use `GET /v1/users?externalId=` to resolve a
          UUID from your own external id.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: orgId
        in: query
        description: Filter to schemas owned by this organization — the Vectros-assigned
          UUID of an organization in your account. Use `GET /v1/orgs?externalId=`
          to resolve a UUID from your own external id.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: surface
        in: query
        description: "Filter to schemas bindable to this surface: record, document,\
          \ user, org, or client. Returns only schemas whose allowed surfaces include\
          \ the given one — useful for listing, say, document types separately from\
          \ record types. The identity surfaces (user, org, client) are account-wide:\
          \ filtering by one lists your account's identity schemas regardless of the\
          \ calling context, whereas record and document list within the calling context."
        schema:
          type: string
        example: document
      - name: recordType
        in: query
        description: "Resolve the single schema for this record type — the natural\
          \ handle for a schema, and the direct alternative to remembering its opaque\
          \ id. Returns a one-element page, or an empty page if no such schema exists.\
          \ Resolved in the calling context for record and document types; combine\
          \ with `surface=user`, `org`, or `client` to resolve an account-wide identity\
          \ schema. Mutually exclusive with `userId`/`orgId` — when supplied, `recordType`\
          \ takes precedence."
        schema:
          type: string
        example: intake_form
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: limit
        in: query
        description: Maximum number of schemas to return per page (1–100; defaults
          to 20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      responses:
        "200":
          description: "A paginated list of schemas, as a `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SchemaPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Schemas
      summary: Create a record schema
      description: "Defines a new record type with optional field definitions, validation\
        \ rules, and lookup indexes. Idempotent by `typeName` within the same ownership\
        \ scope: re-creating an existing `typeName` returns the existing schema rather\
        \ than failing. Requires the `schemas:w` scope."
      operationId: createSchema
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SchemaRequest"
            example:
              typeName: intake_form
              displayName: Client Intake Form
              description: Captures initial client information
              fields:
              - fieldId: first_name
                fieldType: string
                required: true
                searchable: true
              - fieldId: email
                fieldType: string
                required: true
              lookupFields:
              - fieldName: email
                unique: true
              capabilities:
                auditHistory: true
        required: true
      responses:
        "201":
          description: "Schema created, or the existing schema returned when one with\
            \ the same typeName already exists (idempotent)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SchemaResponse"
              example:
                id: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
                typeName: intake_form
                displayName: Client Intake Form
                active: true
                createdAt: 2024-01-15T09:30:00Z
                updatedAt: 2024-01-15T09:30:00Z
        "400":
          description: The request body failed validation.
      security:
        - BearerAuth: []
  /v1/schemas/{id}:

    get:
      tags:
      - Schemas
      summary: Get a record schema by ID
      description: Retrieves a single record schema by its id. Requires the `schemas:r`
        scope.
      operationId: getSchema
      parameters:
      - name: id
        in: path
        description: The id of the schema to retrieve.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      responses:
        "200":
          description: The requested schema.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SchemaResponse"
        "404":
          description: No schema with this id exists in your account.
      security:
        - BearerAuth: []
    put:
      tags:
      - Schemas
      summary: Update a record schema
      description: "Updates a record schema. Fields you omit are preserved; `typeName`\
        \ is immutable and cannot be changed. Collection fields (`fields`, `lookupFields`,\
        \ `renderHints`, `capabilities`) are replaced in full when supplied. Requires\
        \ the `schemas:w` scope."
      operationId: updateSchema
      parameters:
      - name: id
        in: path
        description: The id of the schema to update.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SchemaRequest"
        required: true
      responses:
        "200":
          description: The updated schema.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SchemaResponse"
        "400":
          description: The request body failed validation.
        "404":
          description: No schema with this id exists in your account.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Schemas
      summary: Delete a record schema
      description: "Permanently deletes a record schema. The request is refused with\
        \ 409 if records of this type still exist — delete those records first, since\
        \ every record must reference a live schema. Requires the `schemas:w` scope."
      operationId: deleteSchema
      parameters:
      - name: id
        in: path
        description: The id of the schema to delete.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      responses:
        "204":
          description: The schema was deleted.
        "404":
          description: No schema with this id exists in your account.
        "409":
          description: The schema is still in use by existing records of its type.
      security:
        - BearerAuth: []
  /v1/schemas/{id}/versions:

    get:
      tags:
      - Schemas
      summary: Get a record schema's version history
      description: "Returns the audit trail of changes to a record schema, newest\
        \ first (create, update, and delete). Schema version history is always recorded\
        \ — there is no per-schema toggle to disable it. Requires the `schemas:r`\
        \ scope."
      operationId: getSchemaVersions
      parameters:
      - name: id
        in: path
        description: The id of the schema whose version history you want.
        required: true
        schema:
          type: string
        example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      - name: startFrom
        in: query
        description: Pagination cursor — pass the `nextCursor` value from the previous
          page to fetch the next one.
        schema:
          type: string
      responses:
        "200":
          description: "A paginated version history, as a `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No schema with this id exists in your account.
      security:
        - BearerAuth: []
  /v1/search:

    post:
      tags:
      - Search
      summary: Search your indexed content (documents and records)
      description: "Runs a hybrid, semantic, or text search across the content you\
        \ have indexed. By default it returns both documents and records in a single\
        \ unified result set; pass `contentTypes` to narrow the results (for example\
        \ `[\"documents\"]`). Each result carries a `sourceType` discriminator (`PartnerDocument`\
        \ or `GenericRecord`) so you can branch on the type. Requires the `search:r`\
        \ scope."
      operationId: searchContent
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchRequest"
        required: true
      responses:
        "200":
          description: "The matching results, ranked by relevance."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchResponse"
        "400":
          description: "The request was malformed — for example a missing query, an\
            \ out-of-range limit or offset, an invalid filter key or operator, or\
            \ an unparseable timestamp."
        "403":
          description: "Your token lacks the `search:r` scope, or the ownership filters\
            \ fall outside the scope it was granted."
      security:
        - BearerAuth: []
  /v1/auth/token:

    post:
      tags:
      - Auth
      summary: Mint a scoped bearer token for one of your end users
      description: "Creates a short-lived JWT bearer token restricted to specific\
        \ actions and, optionally, to a particular user, organization, or client.\
        \ Use this to hand a narrowly-scoped credential to a browser or downstream\
        \ service so it never sees your root API key. Only callable with a root API\
        \ key (`sk_*`)."
      operationId: mintToken
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TokenRequest"
            example:
              userId: 550e8400-e29b-41d4-a716-446655440000
              scope:
                allowedActions:
                - records:crud
                - schemas:r
                identity:
                  userId: 550e8400-e29b-41d4-a716-446655440000
                dataScope:
                  orgId:
                  - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
              expiresInSeconds: 3600
        required: true
      responses:
        "200":
          description: The token was minted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MintTokenResponse"
              example:
                token: st_eyJ...
                expiresAt: 1705312200
        "400":
          description: "Invalid request — for example, a missing scope, or a user,\
            \ organization, or client ID that does not exist in your account."
        "403":
          description: Only a root API key can mint scoped tokens.
      security:
        - BearerAuth: []
  /v1/users:

    get:
      tags:
      - Identity
      summary: List users
      description: "Returns a paginated list of the users in your account. Pass `externalId`\
        \ to look up a single user by your own identifier. To filter on schema-declared\
        \ lookup fields, supply `type` and `field` together with one lookup mode:\
        \ `value` (exact match), `from`+`to` (range), or `prefix`. Requires the `users:r`\
        \ scope."
      operationId: listUsers
      parameters:
      - name: externalId
        in: query
        description: "Look up a single user by your own `externalId`. Returns a one-element\
          \ list, or an empty list if no match. Cannot be combined with the `type`/`field`/`value`\
          \ lookup parameters."
        schema:
          type: string
        example: usr_12345
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the `nextCursor` from the previous page
          to fetch the next page; omit it for the first page.
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      - name: limit
        in: query
        description: Maximum number of users to return per page (1–100; defaults to
          20).
        schema:
          maximum: 100
          minimum: 1
          type: integer
          format: int64
          default: 20
        example: 20
      - name: type
        in: query
        description: "The schema record type whose lookup fields you are querying\
          \ (the schema's declared record type, not the user's `HUMAN`/`SERVICE` kind).\
          \ Must be supplied together with `field` and exactly one lookup mode (`value`,\
          \ `from`+`to`, or `prefix`)."
        schema:
          type: string
        example: person_v1
      - name: field
        in: query
        description: The lookup field to filter on. Must be declared as a lookup field
          on the schema named by `type`. Supplied together with `type` and one lookup
          mode.
        schema:
          type: string
        example: team
      - name: value
        in: query
        description: "Exact value to match for `field`. Cannot be combined with `from`/`to`\
          \ or `prefix`. Not allowed for a sensitive (blind-indexed) field on this\
          \ endpoint; use POST /v1/users/lookup instead, which keeps the value out\
          \ of the URL."
        schema:
          type: string
        example: engineering
      - name: from
        in: query
        description: "Inclusive lower bound for a range lookup. Requires `to`, and\
          \ is only valid on non-sensitive fields declared with range support. Cannot\
          \ be combined with `value` or `prefix`."
        schema:
          type: string
        example: 2024-01-01
      - name: to
        in: query
        description: Inclusive upper bound for a range lookup. Requires `from`.
        schema:
          type: string
        example: 2024-12-31
      - name: prefix
        in: query
        description: Prefix to match. Only valid on string fields declared with range
          support. Cannot be combined with `value` or `from`/`to`.
        schema:
          type: string
        example: eng
      - name: order
        in: query
        description: "Sort direction for the matched users: `asc` (ascending, the\
          \ default) or `desc` (descending)."
        schema:
          type: string
          default: asc
          enum:
          - asc
          - desc
        example: asc
      responses:
        "200":
          description: "A page of users in the `{data, nextCursor}` envelope. `nextCursor`\
            \ is null when there are no more pages."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserPage"
      security:
        - BearerAuth: []
    post:
      tags:
      - Identity
      summary: Create or retrieve a user (idempotent by externalId)
      description: "Creates a user identity in your account. The operation is idempotent\
        \ on `externalId`: if a user with the same `externalId` already exists, the\
        \ existing record is returned instead of creating a duplicate. Requires the\
        \ `users:c` scope."
      operationId: createUser
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserRequest"
        required: true
      responses:
        "201":
          description: "The user was created, or an existing user with the same `externalId`\
            \ was returned."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserResponse"
        "400":
          description: "The request body failed validation (for example, `externalId`\
            \ is missing or the payload does not match the bound schema)."
      security:
        - BearerAuth: []
  /v1/users/{id}:

    get:
      tags:
      - Identity
      summary: Get a user by ID
      description: Retrieves a single user by its Vectros-assigned ID. Requires the
        `users:r` scope.
      operationId: getUser
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the user.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      responses:
        "200":
          description: The user was found and returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserResponse"
        "404":
          description: No user with the given ID exists in your account.
      security:
        - BearerAuth: []
    put:
      tags:
      - Identity
      summary: Update a user
      description: "Updates mutable fields on an existing user (such as email, status,\
        \ payload, or schema binding). The `type` field is immutable after creation.\
        \ This endpoint also activates an invited user: a PUT that moves a PENDING\
        \ user to ACTIVE and carries `inviteToken`, `externalSubject`, and `emailVerifiedAttestation=true`\
        \ completes the invitation. Requires the `users:u` scope."
      operationId: updateUser
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the user.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserRequest"
        required: true
      responses:
        "200":
          description: The user was updated and the new state is returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserResponse"
        "404":
          description: No user with the given ID exists in your account.
      security:
        - BearerAuth: []
    delete:
      tags:
      - Identity
      summary: Delete a user
      description: "Permanently deletes a user identity. This cannot be undone. If\
        \ the user is a pending invitation, the associated access profile created\
        \ for that invitation is also removed. Requires the `users:d` scope."
      operationId: deleteUser
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the user.
        required: true
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440000
      responses:
        "204":
          description: The user was deleted. No content is returned.
        "404":
          description: No user with the given ID exists in your account.
      security:
        - BearerAuth: []
  /v1/users/lookup:

    post:
      tags:
      - Identity
      summary: Look up users by field value via the request body (safe for sensitive
        fields)
      description: "Looks up users by a schema lookup field, with the query criteria\
        \ carried in the request body rather than the URL. Use this when looking up\
        \ by a sensitive (blind-indexed) field: the value is blind-indexed server-side\
        \ and never appears in the URL, request logs, or proxies. The query semantics\
        \ are identical to the GET /v1/users lookup, which rejects sensitive-field\
        \ values and directs you here. Returns a page in the `{data, nextCursor}`\
        \ envelope. Requires the `users:r` scope."
      operationId: lookupUsers
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IdentityLookupRequest"
        required: true
      responses:
        "200":
          description: "A page of matching users in the `{data, nextCursor}` envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserPage"
        "400":
          description: "The request body failed validation (for example, `type` or\
            \ `field` is missing, or more than one lookup mode was supplied)."
      security:
        - BearerAuth: []
  /v1/users/{id}/versions:

    get:
      tags:
      - Identity
      summary: Get a user's version history
      description: "Returns the audit trail of changes to a user, most recent first.\
        \ Identity history is always recorded and always available. Sensitive field\
        \ values are redacted in every historical version. Returns a page in the `{data,\
        \ nextCursor}` envelope. Requires the `users:r` scope."
      operationId: getUserVersions
      parameters:
      - name: id
        in: path
        description: The Vectros-assigned UUID of the user.
        required: true
        schema:
          type: string
      - name: startFrom
        in: query
        description: Pagination cursor. Pass the ID of the last version from the previous
          page to fetch the next page.
        schema:
          type: string
      responses:
        "200":
          description: "A page of historical versions in the `{data, nextCursor}`\
            \ envelope."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelDataVersionPage"
        "404":
          description: No user with the given ID exists in your account.
      security:
        - BearerAuth: []
  /v1/users/invite:

    post:
      tags:
      - Auth
      summary: Invite a sub-user by email
      description: "Invite a new member to one of your app contexts by email. Creates\
        \ a pending user with a pre-resolved access profile (their permissions on\
        \ accept) and signs an invitation token. This call is idempotent on the combination\
        \ of context and email: re-inviting the same email in the same context rotates\
        \ the token and resends the invitation rather than creating a duplicate. Returns\
        \ HTTP 201 on a new invite or a successful resend. Returns 409 if that email\
        \ already belongs to an active or suspended member of the app context, or\
        \ already has an identity elsewhere in your account (an email can currently\
        \ belong to only one tenant per account, i.e. your test and live environments\
        \ cannot share an email). When `sendEmail` is false, the response includes\
        \ the raw token and a ready-to-use accept link so you can deliver the invitation\
        \ through your own email provider. Requires the `admin:users` scope."
      operationId: createInvite
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInviteRequest"
        required: true
      responses:
        "201":
          description: "Invitation created, or an existing pending invitation resent\
            \ with a fresh token."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateInviteResponse"
        "400":
          description: "Validation error: malformed email, an `acceptUrl` that is\
            \ not an https domain URL, or a `ttlSeconds` outside the allowed range."
        "403":
          description: Your token lacks the `admin:users` scope.
        "404":
          description: The referenced app context does not exist in your account.
        "409":
          description: "Email already in use: either an active or suspended member\
            \ of this context already has it, or it is already associated with an\
            \ identity elsewhere in your account. Only the second case returns a structured\
            \ body with the `email_already_associated` error code."
      security:
        - BearerAuth: []
  /v1/users/invite/resend:

    post:
      tags:
      - Auth
      summary: Resend a pending invitation
      description: "Resend an outstanding invitation, identified by its email and\
        \ app context. Rotates the invitation token and extends its expiry, then (when\
        \ `sendEmail` is true) re-delivers the email. Rotating the token invalidates\
        \ any previously issued link for this invitation, so only the newest link\
        \ works. The invitee's pending permissions are left unchanged. Requires the\
        \ `admin:users` scope."
      operationId: resendInvite
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInviteRequest"
        required: true
      responses:
        "201":
          description: Invitation resent with a fresh token and extended expiry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateInviteResponse"
        "400":
          description: "Validation error: malformed email, missing context, or an\
            \ invalid `acceptUrl` / `ttlSeconds`."
        "403":
          description: Your token lacks the `admin:users` scope.
        "404":
          description: No pending invitation matches the supplied email and app context.
      security:
        - BearerAuth: []
  /v1/chat:

    post:
      tags:
      - Inference
      summary: Stateless chat inference (streaming)
      description: "Streams a model response as Server-Sent Events (SSE). Send the\
        \ full conversation history in the `messages` array; a message with role `system`\
        \ is extracted and used as the system prompt. Token cost is debited from your\
        \ pre-paid inference balance (in cents), and a small per-call flat fee is\
        \ debited from your monthly platform credit allowance. Requires the `inference:r`\
        \ scope."
      operationId: chatInference
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChatRequest"
        required: true
      responses:
        "200":
          description: Server-Sent Events stream of `content_delta` chunks followed
            by a terminal `done` event.
          content:
            text/event-stream:
              schema:
                $ref: "#/components/schemas/ChatStreamEvent"
        "400":
          description: The request body is malformed or invalid.
        "401":
          description: The API key or bearer token is missing or invalid.
        "402":
          description: "Payment required: your pre-paid inference balance is insufficient,\
            \ your monthly credit allowance is exceeded, or the requested model is\
            \ not available on your plan."
        "403":
          description: You requested global (non-US) region serving (`allowGlobalRegion=true`)
            but your account has no signed global-processing waiver in effect.
        "429":
          description: The burst rate limit was exceeded. Retry after a short delay.
      security:
        - BearerAuth: []
  /v1/documents/{id}/ask:

    post:
      tags:
      - Inference
      summary: Interrogate a single document (streaming)
      description: "Loads a single document's extracted text, supplies it as context,\
        \ and streams a model answer about it. The document must be fully indexed.\
        \ If the document exceeds the 32K-token cap, the call returns 413 with no\
        \ credits charged — use `POST /v1/rag` instead to answer over larger or multi-document\
        \ collections. Requires the `inference:r` scope."
      operationId: documentAsk
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocumentAskRequest"
        required: true
      responses:
        "200":
          description: "Server-Sent Events stream in this order: a `document_context`\
            \ event, one or more `content_delta` chunks, then a terminal `done` event."
          content:
            text/event-stream:
              schema:
                $ref: "#/components/schemas/DocumentAskStreamEvent"
        "400":
          description: "The request body is malformed or invalid (for example, the\
            \ `prompt` field is missing)."
        "401":
          description: The API key or bearer token is missing or invalid.
        "402":
          description: "Payment required: your pre-paid inference balance is insufficient,\
            \ your monthly credit allowance is exceeded, or the requested model is\
            \ not available on your plan."
        "403":
          description: You requested global (non-US) region serving (`allowGlobalRegion=true`)
            but your account has no signed global-processing waiver in effect.
        "404":
          description: No document with the given id exists in your account.
        "409":
          description: "The document is still processing, failed to process, or its\
            \ extracted text was not retained (`storeText=false`), so it cannot be\
            \ interrogated."
        "413":
          description: The document exceeds the 32K-token cap. Use `POST /v1/rag`
            instead.
        "429":
          description: The burst rate limit was exceeded. Retry after a short delay.
      security:
        - BearerAuth: []
  /v1/rag:

    post:
      tags:
      - Inference
      summary: "RAG inference: hybrid search + streaming LLM answer"
      description: "Runs hybrid search over your indexed content, then streams a model\
        \ answer grounded in the top results. The SSE stream emits a `search_results`\
        \ event first (carrying the matched results and their metadata), an optional\
        \ `truncation_warning` if lower-scoring results were dropped to fit the model's\
        \ context window, then `content_delta` chunks, and finally a terminal `done`\
        \ event. Requires the `inference:r` scope."
      operationId: ragInference
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RagRequest"
        required: true
      responses:
        "200":
          description: "Server-Sent Events stream in this order: a `search_results`\
            \ event, an optional `truncation_warning`, one or more `content_delta`\
            \ chunks, then a terminal `done` event."
          content:
            text/event-stream:
              schema:
                $ref: "#/components/schemas/RagStreamEvent"
        "400":
          description: "The request body is malformed or invalid (for example, the\
            \ `query` field is missing)."
        "401":
          description: The API key or bearer token is missing or invalid.
        "402":
          description: "Payment required: your pre-paid inference balance is insufficient,\
            \ your monthly credit allowance is exceeded, or the requested model is\
            \ not available on your plan."
        "403":
          description: You requested global (non-US) region serving (`allowGlobalRegion=true`)
            but your account has no signed global-processing waiver in effect.
        "429":
          description: The burst rate limit was exceeded. Retry after a short delay.
        "500":
          description: The search infrastructure encountered an error and the query
            could not be completed.
      security:
        - BearerAuth: []
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: "Two credential types are accepted. API keys (sk_live_* or sk_test_*) are long-lived server-to-server credentials generated in the developer portal. Scoped tokens (st_*) are short-lived JWTs minted via POST /v1/auth/token - use these to grant end-users direct API access without exposing your API key. Both are passed as: Authorization: Bearer <credential>"

  schemas:
    Scalar:
      description: "A scalar filter operand — a string, number, or boolean."
      oneOf:
        - type: string
        - type: number
        - type: boolean
    FilterCondition:
      type: object
      description: "Operator map for one field. Operators are AND-combined; $in/$nin take an array operand and may not be combined with other operators. Mirrors the documented closed operator set on SearchRequest.filters."
      properties:
        $eq:
          description: "Equals the given scalar."
          $ref: '#/components/schemas/Scalar'
        $ne:
          description: "Not equal to the given scalar."
          $ref: '#/components/schemas/Scalar'
        $gt:
          description: "Strictly greater than the given scalar."
          $ref: '#/components/schemas/Scalar'
        $gte:
          description: "Greater than or equal to the given scalar."
          $ref: '#/components/schemas/Scalar'
        $lt:
          description: "Strictly less than the given scalar."
          $ref: '#/components/schemas/Scalar'
        $lte:
          description: "Less than or equal to the given scalar."
          $ref: '#/components/schemas/Scalar'
        $in:
          description: "Matches any value in the array (membership)."
          type: array
          items:
            $ref: '#/components/schemas/Scalar'
        $nin:
          description: "Matches none of the values in the array (negated membership)."
          type: array
          items:
            $ref: '#/components/schemas/Scalar'
    FilterValue:
      description: "A field filter value: a scalar (equality), an array of scalars (OR-set / match-any), or an operator map (range / negation / membership)."
      oneOf:
        - type: string
        - type: number
        - type: boolean
        - type: array
          items:
            $ref: '#/components/schemas/Scalar'
        - $ref: '#/components/schemas/FilterCondition'
    Jwk:
      type: object
      properties:
        kty:
          type: string
          description: Key type. Always `EC` for this P-256 key.
          example: EC
        crv:
          type: string
          description: Curve identifier. Always `P-256`.
          example: P-256
        x:
          type: string
          description: "The x coordinate of the EC public key, base64url-encoded (32\
            \ bytes, unpadded)."
          example: f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU
        "y":
          type: string
          description: "The y coordinate of the EC public key, base64url-encoded (32\
            \ bytes, unpadded)."
          example: x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0
        use:
          type: string
          description: Intended use of the key. `sig` indicates signature verification.
          example: sig
        kid:
          type: string
          description: "Key id that uniquely identifies this key, so clients can cache\
            \ it and reload when it rotates."
          example: vectros-jwt-key-001
        alg:
          type: string
          description: Signing algorithm. `ES256` is ECDSA using P-256 and SHA-256.
          example: ES256
      description: A single JSON Web Key entry. Currently an EC key on the P-256 curve.
    JwksResponse:
      type: object
      properties:
        keys:
          type: array
          description: The set of JSON Web Keys. Today this contains a single P-256
            EC public key; the array can hold more than one key during a rotation.
          items:
            $ref: "#/components/schemas/Jwk"
      description: A standard JWKS document (RFC 7517) containing the public-key material
        used to verify platform-signed JWTs.
    ReadAccessLogPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/ReadAccessLogResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of PHI read-access (accounting-of-disclosures) rows — a\
        \ `{data, nextCursor}` envelope. `nextCursor` is an opaque cursor, null when\
        \ no more pages remain; pass it back as the `startFrom` query param to fetch\
        \ the next page."
    ReadAccessLogResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for this read-access row.
          example: 550e8400-e29b-41d4-a716-446655440000
        contextId:
          type: string
          description: The app context the read occurred in (the data-partition axis).
          example: ctx_intake
        subjectType:
          type: string
          description: "The kind of subject whose PHI was read: `user`, `client`,\
            \ or `org`."
          example: user
          enum:
          - user
          - client
          - org
        subjectId:
          type: string
          description: Identifier of the data subject whose PHI was read.
          example: user_abc123
        clientId:
          type: string
          description: "Identifier of the nested client/patient the read was scoped\
            \ to, when applicable."
          example: client_xyz789
        callerKeyId:
          type: string
          description: The credential (API key / scoped token key id) that performed
            the read — the actor.
          example: key_abc123
        action:
          type: string
          description: "What the caller did: `read` (single fetch), `list`, `lookup`,\
            \ `search`, or `rag`."
          example: read
          enum:
          - read
          - list
          - lookup
          - search
          - rag
        resourceType:
          type: string
          description: "The accessed resource's type: a record schema type name, or\
            \ one of `document`, `search`, or `rag`."
          example: intake_form
        resourceId:
          type: string
          description: The accessed record/document id (opaque). Never the payload
            or looked-up value.
          example: rec_456
        revealedSensitive:
          type: boolean
          description: True only if at least one sensitive field was actually returned
            unmasked in this read — not merely that the caller's token carried scope
            to read sensitive fields. The revealed value itself is never recorded.
          example: false
        auditDisposition:
          type: string
          description: "The retention disposition this row was recorded under: `retain`,\
            \ `retain-redacted`, or `delete`."
          example: retain-redacted
        retainUntil:
          type: string
          description: "The earliest this row may be disposed of, recorded at write\
            \ time (ISO-8601 UTC). Advisory only — it is not an automatic expiry.\
            \ Null when no retention floor applies."
          example: 2031-01-15T09:30:00Z
        recipientRef:
          type: string
          description: "Reserved for future use — not yet populated, so this is currently\
            \ always null. When implemented, it will record the basis for the access\
            \ (e.g. treatment, payment, operations, or an external disclosure) to\
            \ support a §164.528 accounting."
          example: treatment
        createdAt:
          type: string
          description: When the read occurred / the row was recorded (ISO-8601 UTC).
          example: 2024-01-15T09:30:00Z
      description: "A single PHI read-access (accounting-of-disclosures) row: who\
        \ read which subject's data, when, and against which record. Metadata only\
        \ — never the PHI itself. Returned by `GET /v1/admin/access-log`."
    ScopedKeyResponse:
      type: object
      properties:
        keyId:
          type: string
          description: "Server-generated identifier for the key, stable for its entire\
            \ lifetime. Pass this in the URL path of the get and revoke endpoints."
          example: 550e8400-e29b-41d4-a716-446655440000
        rawKey:
          type: string
          description: The raw secret key. Returned ONLY in the response to creating
            the key; all subsequent reads omit this field. Treat it as a sensitive
            credential and store it securely.
          example: ssk_live_AbCdEf1234567890...
        keyName:
          type: string
          description: The human-readable name you gave the key when creating it.
          example: research-bot production
        label:
          type: string
          description: "Optional display label for the key, surfaced through `/v1/ping`\
            \ as `principalLabel`. Omitted when no label was set."
          example: Claude Desktop — clinical-notes RO
        tenantId:
          type: string
          description: The tenant the key operates on — either your live or your test
            environment.
          example: ten_live_xxx
        contextId:
          type: string
          description: The app context the key is bound to.
          example: myapp
        userId:
          type: string
          description: The user the key acts as. This is the same user ID used by
            the `/v1/users` endpoints.
          example: alice-001
        userType:
          type: string
          description: "The bound user's type: `HUMAN` or `SERVICE`. Included as a\
            \ convenience for filtering in admin interfaces."
          example: SERVICE
          enum:
          - HUMAN
          - SERVICE
        status:
          type: string
          description: "The key's lifecycle status: `active` or `revoked`."
          example: active
          enum:
          - active
          - revoked
        keyType:
          type: string
          description: Always `scoped` for keys created through this endpoint. The
            field exists so this response shape can also represent unscoped (`sk_*`)
            keys.
          example: scoped
          enum:
          - scoped
        accessProfileRef:
          type: string
          description: "Reference to the bound access profile, in the form `tenant#context#usr_user`\
            \ (the user segment carries the `usr_` prefix). Useful for cross-referencing\
            \ the profile via `/v1/app-contexts/{contextId}/profiles/{principalId}`."
          example: ten_live_xxx#myapp#usr_alice-001
        createdAt:
          type: string
          description: "When the key was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        revokedAt:
          type: string
          description: "When the key was revoked, as an ISO-8601 UTC timestamp. Present\
            \ only on revoked keys."
          example: 2024-06-01T14:00:00Z
        lastUsedAt:
          type: string
          description: "When the key was most recently used, as an ISO-8601 UTC timestamp.\
            \ This value is coarse (updated at most about once an hour) and best-effort.\
            \ Null if the key has not been used since usage tracking began."
          example: 2024-06-02T11:05:00Z
      description: Metadata for a scoped API key (`ssk_*`).
    CreateScopedKeyRequest:
      required:
      - contextId
      - keyName
      - tenantId
      - userId
      type: object
      properties:
        keyName:
          type: string
          description: "A human-readable name for the key, used to identify and disambiguate\
            \ multiple keys for the same user. Maximum 100 characters."
          example: research-bot production
        tenantId:
          type: string
          description: The tenant the key will operate on. Must be your live tenant
            ID or your test tenant ID; an ID outside your account returns a uniform
            `404 not found` (no distinction is made between a missing and an out-of-account
            tenant).
          example: ten_live_xxx
        contextId:
          type: string
          description: The app context within the tenant. Must reference an app context
            that already exists (create one via `POST /v1/app-contexts`).
          example: myapp
        userId:
          type: string
          description: The user the key binds to. May be a `HUMAN` or a `SERVICE`
            user (a service user is the typical agent or bot case). An access profile
            must already exist for this context and user — the key references that
            profile; it does not create one.
          example: alice-001
        label:
          type: string
          description: "Optional display label surfaced through `/v1/ping` as `principalLabel`.\
            \ The MCP `current_identity` tool uses it to show \"signed in as ...\"\
            \ hints to end users (e.g. \"Claude Desktop — clinical-notes RO\"). Distinct\
            \ from `keyName`, which is your own identifier for the key. Maximum 80\
            \ characters; if present, it must not have leading or trailing whitespace."
          example: Claude Desktop — clinical-notes RO
      description: Request body for creating a new scoped API key bound to an existing
        access profile.
    ScopedKeyPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/ScopedKeyResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A paginated list of scoped API keys, returned as a `{data, nextCursor}`\
        \ envelope. `nextCursor` is an opaque cursor for fetching the next page, and\
        \ is null when no more pages remain."
    AdminLogsResponse:
      required:
      - entries
      - queryDurationMs
      - tenantId
      - truncated
      type: object
      properties:
        entries:
          type: array
          description: "Matching log entries, most recent first. Bounded by the request\
            \ `limit` (default 200, max 500)."
          items:
            $ref: "#/components/schemas/LogEntry"
        truncated:
          type: boolean
          description: "True if more matching rows exist than the requested limit.\
            \ When true, narrow the time window or add a filter (resource/method/keyId)\
            \ to see older entries."
          example: false
        queryDurationMs:
          type: integer
          description: "Wall-clock time spent retrieving these results, in milliseconds.\
            \ Useful for diagnosing slow queries (large time windows or high-traffic\
            \ accounts)."
          format: int64
          example: 1240
        tenantId:
          type: string
          description: "Echoes your account id, confirming which account these entries\
            \ belong to."
          example: tnt_abc123
      description: A page of API call log entries for your account within a time window.
        Each entry represents one API request. Request and response bodies are never
        logged.
    LogEntry:
      required:
      - method
      - resource
      - status
      - timestamp
      type: object
      properties:
        timestamp:
          type: string
          description: When the API call completed (ISO-8601 UTC).
          example: 2026-05-26T14:23:11.482Z
        method:
          type: string
          description: HTTP method of the request.
          example: POST
        resource:
          type: string
          description: "Top-level resource the call targeted (one of `documents`,\
            \ `records`, `search`, `schemas`, `folders`, `clients`, `orgs`, `users`,\
            \ `usage`, `auth`, `models`, `ping`, `rag`, `chat`, `ask`, `erasure-requests`,\
            \ or `export`), derived from the request path."
          example: records
        contextId:
          type: string
          description: The app context the request operated under — the context partition
            the credential carried. `*` for a root/cross-context caller. Null for
            older log lines or for entry points that are not context-scoped.
          nullable: true
          example: ctx_intake
        status:
          type: integer
          description: HTTP response status code.
          format: int32
          example: 201
        keyId:
          type: string
          description: ID of the API key or scoped token that issued the request.
            Null if the request was unauthenticated (extremely rare — most paths require
            auth before logging).
          nullable: true
          example: key_abc123
        durationMs:
          type: integer
          description: "Total request duration in milliseconds, from auth to response\
            \ completion."
          format: int64
          example: 145
        path:
          type: string
          description: Full request path.
          example: /v1/records/550e8400-e29b-41d4-a716-446655440000
      description: A single API call as recorded after the request completed.
    AccessProfileResponse:
      type: object
      properties:
        id:
          type: string
          description: Opaque identifier for this profile. Also serves as the pagination
            cursor.
          example: tenant-live-abc#myapp#usr_alice
        contextId:
          type: string
          description: The app context this profile belongs to.
          example: myapp
        principalId:
          type: string
          description: "Principal this profile applies to — `usr_<userId>` for a user,\
            \ or `key_<keyId>` for a scoped API key acting as its own principal."
          example: usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8
        scopes:
          type: array
          description: Inline scope clauses granted to the principal. Mutually exclusive
            with `roleId` — exactly one of the two is present.
          items:
            $ref: "#/components/schemas/ScopeClause"
        roleId:
          type: string
          description: Reference to a role that supplies this principal's scopes.
            Mutually exclusive with `scopes` — exactly one of the two is present.
        identityOverrides:
          type: object
          additionalProperties: true
          description: Per-context identity overrides. Only `orgId` and `clientId`
            may be overridden.
        status:
          type: string
          description: "Profile lifecycle status: `active` or `suspended`."
          example: active
        createdAt:
          type: string
          description: When the profile was created (ISO-8601 UTC).
          example: 2024-01-15T09:30:00Z
        lastModified:
          type: string
          description: When the profile was last modified (ISO-8601 UTC).
          example: 2024-01-15T09:35:00Z
      description: An access profile — the permission row for one principal within
        one app context.
    ScopeClause:
      required:
      - allowed_actions
      type: object
      properties:
        allowed_actions:
          type: array
          description: "Verbs this clause grants. Custom verbs are allowed, but a\
            \ standard catalog is defined: '*' (wildcard), 'read', 'write', 'delete',\
            \ 'admin:keys', 'admin:profiles', 'logs:r', 'create_own_scoped_key'. Entries\
            \ may also use the compact 'resource:cruds[:qualifier]' form, e.g. 'documents:cru'\
            \ or 'records:rs:patient'. The 's' letter in that form grants sensitive-field\
            \ REVEAL (PHI un-masking) for the given record-type — fail-safe: absent\
            \ 's', sensitive fields are masked at the response boundary."
          example:
          - read
          - write
          items:
            type: string
            description: "Verbs this clause grants. Custom verbs are allowed, but\
              \ a standard catalog is defined: '*' (wildcard), 'read', 'write', 'delete',\
              \ 'admin:keys', 'admin:profiles', 'logs:r', 'create_own_scoped_key'.\
              \ Entries may also use the compact 'resource:cruds[:qualifier]' form,\
              \ e.g. 'documents:cru' or 'records:rs:patient'. The 's' letter in that\
              \ form grants sensitive-field REVEAL (PHI un-masking) for the given\
              \ record-type — fail-safe: absent 's', sensitive fields are masked at\
              \ the response boundary."
            example: "[\"read\",\"write\"]"
        data_scope:
          type: object
          additionalProperties:
            type: object
            description: "Attribute filters narrowing data this clause applies to.\
              \ Empty object = applies to ALL data within the tenant. Specific values\
              \ narrow; include null in an allowed-value list (e.g. {\"orgId\": [\"\
              orgX\", null]}) to ALSO grant access to tenant-level records that have\
              \ no value set for that attribute. Any scopable attribute name is valid\
              \ as a key."
            example:
              orgId:
              - org_engineering
          description: "Attribute filters narrowing data this clause applies to. Empty\
            \ object = applies to ALL data within the tenant. Specific values narrow;\
            \ include null in an allowed-value list (e.g. {\"orgId\": [\"orgX\", null]})\
            \ to ALSO grant access to tenant-level records that have no value set\
            \ for that attribute. Any scopable attribute name is valid as a key."
          example:
            orgId:
            - org_engineering
      description: "Inline list of scope clauses granted directly to the invitee.\
        \ Provide this OR `roleId`, not both."
    AccessProfileRequest:
      required:
      - principalId
      type: object
      properties:
        principalId:
          type: string
          description: "Principal this profile applies to. Must start with `usr_`\
            \ (an authenticated user — the suffix is the user id) or `key_` (a scoped\
            \ API key acting as its own principal — the suffix is the key id). The\
            \ suffix may contain only letters, digits, underscores, and hyphens. Required\
            \ when creating (POST); ignored when updating (PUT), where it is taken\
            \ from the path."
          example: usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8
        scopes:
          type: array
          description: "Inline scope clauses to grant the principal. Provide exactly\
            \ one of `scopes` or `roleId` — setting both, or neither, returns a 400."
          items:
            $ref: "#/components/schemas/ScopeClause"
        roleId:
          type: string
          description: "Reference to a role within the same context that supplies\
            \ this principal's scopes. Provide exactly one of `scopes` or `roleId`\
            \ — setting both, or neither, returns a 400. Changes to the role's scopes\
            \ take effect for all referencing profiles."
          example: engineering-member
        identityOverrides:
          type: object
          additionalProperties: true
          description: Optional per-context identity overrides. Only `orgId` and `clientId`
            may be overridden; any other key is rejected. An empty or omitted map
            applies no overrides — the base identity from the user or key record is
            used as-is.
          example:
            orgId: org_engineering
        status:
          type: string
          description: Profile lifecycle status. `active` permits token minting; `suspended`
            denies it (minting returns a uniform 403). Defaults to `active` when omitted.
          example: active
          enum:
          - active
          - suspended
      description: Request body for creating or updating an access profile.
    AppContextResponse:
      type: object
      properties:
        id:
          type: string
          description: "Opaque composite identifier for this app context. Use it as\
            \ the pagination cursor (pass it as `startFrom` to page through results);\
            \ for everything else, refer to the app context by its `contextId`."
          example: tenant-live-abc#myapp
        contextId:
          type: string
          description: Your identifier for the app context.
          example: myapp
        name:
          type: string
          description: Human-readable display name for the app context.
          example: My Internal App
        description:
          type: string
          description: Free-text description of the app context.
          example: Internal engineering tooling
        createdAt:
          type: string
          description: "When the app context was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        lastModified:
          type: string
          description: "When the app context was last modified, as an ISO-8601 UTC\
            \ timestamp."
          example: 2024-01-15T09:35:00Z
        status:
          type: string
          description: "Lifecycle status of the app context: `active` under normal\
            \ operation; `purging` while an asynchronous delete is draining the context's\
            \ data; `deleted` once that teardown completes. After you call DELETE\
            \ on an app context (which returns 202 and drains asynchronously), poll\
            \ this field to observe when the teardown has finished."
          example: active
          enum:
          - active
          - purging
          - deleted
      description: "An app context: a namespace you define to group access profiles\
        \ and roles for one of your applications."
    AppContextRequest:
      required:
      - contextId
      - name
      type: object
      properties:
        contextId:
          type: string
          description: "Your identifier for the app context. Must be 3-31 characters,\
            \ start with a lowercase letter, and contain only lowercase letters, digits,\
            \ and hyphens (for example `myapp`, `taskflow`, or `engineering-team`).\
            \ Required when creating a context. Ignored when updating, since the URL\
            \ path supplies the contextId on update."
          example: myapp
        name:
          type: string
          description: Human-readable display name for this app context. Required.
          example: My Internal App
        description:
          type: string
          description: Optional free-text description of what this app context is
            for.
          example: Internal tooling for the engineering team
      description: Request body for creating or updating an app context.
    RoleResponse:
      type: object
      properties:
        id:
          type: string
          description: "The role's globally unique identifier, composed from your\
            \ account, the app context, and the role ID. Pass this value as the pagination\
            \ cursor to fetch the next page."
          example: tenant-live-abc#myapp#engineering-member
        contextId:
          type: string
          description: The ID of the app context this role belongs to.
          example: myapp
        roleId:
          type: string
          description: Your own identifier for this role within the app context.
          example: engineering-member
        name:
          type: string
          description: A human-readable display name for the role.
          example: Engineering Team Member
        description:
          type: string
          description: An optional free-text description of what the role grants.
        scopes:
          type: array
          description: "The role's permissions, expressed as one or more scope clauses.\
            \ An action is permitted if any clause allows it and that clause's data\
            \ scope matches the target."
          items:
            $ref: "#/components/schemas/ScopeClause"
        createdAt:
          type: string
          description: "When the role was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        lastModified:
          type: string
          description: "When the role was last modified, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:35:00Z
      description: "A role: a named, reusable set of scope clauses that can be assigned\
        \ to users independently of any one identity."
    RoleRequest:
      required:
      - name
      - roleId
      - scopes
      type: object
      properties:
        roleId:
          type: string
          description: "Your own identifier for the role. Must be 3 to 31 characters,\
            \ starting with a lowercase letter and containing only lowercase letters,\
            \ digits, and dashes (the same rules as a context ID). Required when creating\
            \ a role; ignored when updating, since the role ID is taken from the URL\
            \ path."
          example: engineering-member
        name:
          type: string
          description: A human-readable display name for the role.
          example: Engineering Team Member
        description:
          type: string
          description: An optional free-text description of what the role grants.
          example: Read-only access to engineering records and write access to own
            user data
        scopes:
          type: array
          description: "The role's permissions, as one or more scope clauses. Each\
            \ clause has `allowed_actions` (a list of permitted verbs) and `data_scope`\
            \ (an attribute filter that restricts which records the actions apply\
            \ to). An action is permitted if any clause allows that action and that\
            \ clause's data scope matches the target record. To include records whose\
            \ ownership field is null (account-level shared records), add `null` to\
            \ the allowed values for that field."
          items:
            $ref: "#/components/schemas/ScopeClause"
      description: Request body for creating or updating a role.
    ChangedField:
      type: object
      properties:
        name:
          type: string
          description: Name of the changed field.
        sourceVal:
          type: object
          description: Field value before the change (type depends on the field).
        targetVal:
          type: object
          description: Field value after the change (type depends on the field).
      description: A single field's before/after values within an audit-history diff.
    ModelChanges:
      type: object
      properties:
        summary:
          type: string
          description: "Human-readable summary of the change, e.g. \"Changed: firstName,\
            \ email\"."
        fields:
          type: array
          description: Names of the fields that changed.
          items:
            type: string
            description: Names of the fields that changed.
        count:
          type: integer
          description: Number of fields that changed.
          format: int32
        details:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/ChangedField"
          description: "Per-field before/after values, keyed by field name."
        error:
          type: string
          description: Error message if the diff could not be computed; null on success.
      description: "Field-level diff for an audited change: a summary, the list of\
        \ changed field names, a count, and per-field before/after detail."
    ModelDataVersionPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/ModelDataVersionResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A single page of version-history entries, returned by the `.../versions`\
        \ endpoints. The `data` array holds the entries. `nextCursor` is an opaque\
        \ cursor you pass to fetch the next page; it is null when no more pages remain."
    ModelDataVersionResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for this version-history entry.
          example: 550e8400-e29b-41d4-a716-446655440000
        changeType:
          type: string
          description: "The kind of change this entry records: CREATE for the initial\
            \ creation, UPDATE for a mutation, DELETE for a deletion."
          example: UPDATE
          enum:
          - CREATE
          - UPDATE
          - DELETE
        previousContent:
          type: string
          description: "A JSON-encoded snapshot of the record's full state immediately\
            \ before this change. Deserialize it to inspect the previous payload,\
            \ ownership, and other fields. Present on UPDATE and DELETE entries; null\
            \ on CREATE."
          example: "{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"payload\":{\"\
            notes\":\"original note\"}}"
        previousVersion:
          type: integer
          description: The record's version number immediately before this change.
            Null on CREATE.
          format: int64
          example: 1
        changeReason:
          type: string
          description: "A short, system-generated description of what changed."
          example: "Updated: recordType=note"
        changedBy:
          type: string
          description: Identifier of the user or API key that made the change.
          example: user_abc123
        createdAt:
          type: string
          description: "Time this version-history entry was recorded, as an ISO-8601\
            \ UTC timestamp."
          example: 2024-01-15T09:30:00Z
        changedFields:
          $ref: "#/components/schemas/ModelChanges"
      description: "A single entry in a record's audit-trail version history. Returned\
        \ by GET /v1/records/{id}/versions."
    AccessProfilePage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/AccessProfileResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of access profiles — a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor, null when no more pages remain; pass it back to fetch\
        \ the next page."
    AppContextPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/AppContextResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of app contexts as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    RolePage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/RoleResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of roles as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    ContextDetail:
      type: object
      properties:
        contextId:
          type: string
          description: AppContext id (the contextId data is partitioned under).
          example: default
        tenantId:
          type: string
          description: "Your account (tenant) id for the environment this context\
            \ belongs to, as a UUID."
        tenantMode:
          type: string
          description: "Environment of the owning account: \"live\" or \"test\"."
          example: live
        credits:
          $ref: "#/components/schemas/TenantCredits"
        search:
          $ref: "#/components/schemas/SearchSection"
        documents:
          $ref: "#/components/schemas/DocumentsSection"
        records:
          $ref: "#/components/schemas/RecordsSection"
        inference:
          $ref: "#/components/schemas/TenantInferenceSection"
      description: Full usage breakdown for a single AppContext within one of your
        environments.
    CountAndCredits:
      type: object
      properties:
        count:
          type: integer
          description: Number of operations this period
          format: int64
          example: 120
        credits:
          type: integer
          description: Credits charged for these operations (rounded down)
          format: int64
          example: 240
        creditsMilli:
          type: integer
          description: "credits in milli-credit precision (exact, 1 credit = 1000\
            \ milli-credits)"
          format: int64
          example: 240000
      description: Operation count and the credits charged for them
    CreditBreakdown:
      type: object
      properties:
        searchQueries:
          type: integer
          description: Credits consumed by search queries
          format: int64
          example: 4200
        searchQueriesMilli:
          type: integer
          description: searchQueries in milli-credit precision (exact)
          format: int64
          example: 4200000
        searchIngest:
          type: integer
          description: Credits consumed by ingesting documents/records into search
            indices
          format: int64
          example: 3100
        searchIngestMilli:
          type: integer
          description: searchIngest in milli-credit precision (exact)
          format: int64
          example: 3100000
        documents:
          type: integer
          description: Credits consumed by document downloads
          format: int64
          example: 0
        documentsMilli:
          type: integer
          description: documents in milli-credit precision (exact)
          format: int64
          example: 0
        records:
          type: integer
          description: Credits consumed by record writes
          format: int64
          example: 500
        recordsMilli:
          type: integer
          description: records in milli-credit precision (exact)
          format: int64
          example: 500000
        identity:
          type: integer
          description: Credits consumed by identity (user/org/client) writes
          format: int64
          example: 120
        identityMilli:
          type: integer
          description: identity in milli-credit precision (exact)
          format: int64
          example: 120000
        storageEstimate:
          type: integer
          description: Estimated storage credits this period (final billing runs at
            cycle end)
          format: int64
          example: 4700
        storageEstimateMilli:
          type: integer
          description: storageEstimate in milli-credit precision (exact)
          format: int64
          example: 4700000
      description: Per-category credit decomposition for the period. Each category
        has both a rounded value (for display) and a *Milli value (for exact math).
    Credits:
      type: object
      properties:
        used:
          type: integer
          description: Credits consumed this period (rounded down to whole credits)
          format: int64
          example: 12500
        usedMilli:
          type: integer
          description: Credits consumed this period in milli-credit precision (1 credit
            = 1000 milli-credits). Use this for exact accounting and for cross-checking
            per-environment totals — the rounded `used` value can differ from `live.used
            + test.used` by up to 1 credit per category due to independent truncation.
            The milli value is exact.
          format: int64
          example: 12500000
        limit:
          type: integer
          description: Plan monthly credit limit. Null for unlimited (enterprise)
            plans.
          format: int64
          nullable: true
          example: 100000
        remaining:
          type: integer
          description: Remaining credits before plan limit hit. Null for unlimited
            plans.
          format: int64
          nullable: true
          example: 87500
        breakdown:
          $ref: "#/components/schemas/CreditBreakdown"
      description: Aggregate credit usage for your account this period
    DocumentIngest:
      type: object
      properties:
        text:
          $ref: "#/components/schemas/CountAndCredits"
        file:
          $ref: "#/components/schemas/CountAndCredits"
      description: Document ingest counts by path
    DocumentStorage:
      type: object
      properties:
        text:
          $ref: "#/components/schemas/StorageEntry"
        file:
          $ref: "#/components/schemas/StorageEntry"
      description: Document storage bytes by ingest path
    DocumentsSection:
      type: object
      properties:
        ingest:
          $ref: "#/components/schemas/DocumentIngest"
        secondaryStorageBytes:
          $ref: "#/components/schemas/DocumentStorage"
        downloads:
          $ref: "#/components/schemas/Downloads"
        writes:
          $ref: "#/components/schemas/WriteStats"
        primaryStorageBytes:
          $ref: "#/components/schemas/StorageEntry"
      description: Document activity breakdown
    Downloads:
      type: object
      properties:
        bytes:
          type: integer
          description: Total bytes egressed via download URLs this period
          format: int64
          example: 104857600
        credits:
          type: integer
          description: Credits charged for download egress (rounded up to whole GB)
          format: int64
          example: 1
      description: "Pre-signed download URL usage. Note: downloads are billed at GB\
        \ granularity (ceiling), so per-environment `credits` values can sum to more\
        \ than the account-level `credits` when each environment has a sub-GB remainder.\
        \ The `bytes` field is exact — use `ceil((live.bytes + test.bytes) / 1e9)\
        \ * DOWNLOAD_CREDITS_PER_GB` for precise math."
    IdentitySection:
      type: object
      properties:
        users:
          $ref: "#/components/schemas/IdentitySurface"
        orgs:
          $ref: "#/components/schemas/IdentitySurface"
        clients:
          $ref: "#/components/schemas/IdentitySurface"
      description: "Identity write + storage activity, broken down per identity surface.\
        \ Each surface mirrors the record shape: a write count (base + schema-lookup-field\
        \ writes) and a storage line at the record rate."
    IdentitySurface:
      type: object
      properties:
        writes:
          $ref: "#/components/schemas/WriteStats"
        primaryStorageBytes:
          $ref: "#/components/schemas/StorageEntry"
      description: Write counts and storage for one identity surface
    InferenceCallStats:
      type: object
      properties:
        calls:
          type: integer
          description: Number of inference calls this period
          format: int64
          example: 150
        inputTokens:
          type: integer
          description: Total input tokens consumed across all calls
          format: int64
          example: 45000
        outputTokens:
          type: integer
          description: Total output tokens generated across all calls
          format: int64
          example: 12000
      description: "Inference call counters: invocations and token throughput"
    InferenceEndpoints:
      type: object
      properties:
        chat:
          $ref: "#/components/schemas/InferenceCallStats"
        rag:
          $ref: "#/components/schemas/InferenceCallStats"
        ask:
          $ref: "#/components/schemas/InferenceCallStats"
      description: Per-endpoint inference call stats
    InferenceSection:
      type: object
      properties:
        balanceCents:
          type: integer
          description: Pre-paid inference balance in cents (used to settle model token
            costs)
          format: int64
          example: 12500
        endpoints:
          $ref: "#/components/schemas/InferenceEndpoints"
        totals:
          $ref: "#/components/schemas/InferenceCallStats"
      description: Inference activity at the account level (includes pre-paid balance)
    IngestCounts:
      type: object
      properties:
        text:
          $ref: "#/components/schemas/IngestEntry"
        semantic:
          $ref: "#/components/schemas/IngestEntry"
        hybrid:
          $ref: "#/components/schemas/IngestEntry"
        parentChunks:
          $ref: "#/components/schemas/IngestEntry"
      description: Search ingest activity by mode
    IngestEntry:
      type: object
      properties:
        count:
          type: integer
          description: Number of ingest operations
          format: int64
          example: 45
        bytes:
          type: integer
          description: Bytes ingested
          format: int64
          example: 152340
        credits:
          type: integer
          description: Credits charged for these ingests (rounded down)
          format: int64
          example: 200
        creditsMilli:
          type: integer
          description: credits in milli-credit precision (exact)
          format: int64
          example: 200000
      description: "Ingest count, bytes, and credits for a single mode"
    QueryCounts:
      type: object
      properties:
        text:
          $ref: "#/components/schemas/CountAndCredits"
        semantic:
          $ref: "#/components/schemas/CountAndCredits"
        hybrid:
          $ref: "#/components/schemas/CountAndCredits"
      description: Query counts and credits by search mode
    ReadAccessSection:
      type: object
      properties:
        storageBytes:
          $ref: "#/components/schemas/StorageEntry"
        rows:
          type: integer
          description: Logged-read write/operation count this period (one per emitted
            access record). Recovers the additional per-read write cost. Credits are
            0 until this rate is set.
          format: int64
        rowCredits:
          type: integer
          description: Credits for the logged-read writes this period (0 until this
            rate is set).
          format: int64
      description: "PHI read-access logging metering. Two lines, because read-access\
        \ bills BOTH the retained storage AND the per-read write (unlike audit history's\
        \ storage-only line — an audited write is already billed; a read is not)."
    RecordWrites:
      type: object
      properties:
        count:
          type: integer
          description: Base record writes (one per create/update)
          format: int64
          example: 300
        indexCount:
          type: integer
          description: "Logical index credits maintained by these writes — ownership\
            \ + externalId + schema lookup fields (an equality index counts 1, a range-enabled\
            \ index counts 3)."
          format: int64
          example: 450
        credits:
          type: integer
          description: Credits charged for all record writes this period (rounded
            down)
          format: int64
          example: 750
        creditsMilli:
          type: integer
          description: credits in milli-credit precision (exact)
          format: int64
          example: 750000
      description: Record write counts and credits
    RecordsSection:
      type: object
      properties:
        writes:
          $ref: "#/components/schemas/RecordWrites"
        primaryStorageBytes:
          $ref: "#/components/schemas/StorageEntry"
        secondaryStorageBytes:
          $ref: "#/components/schemas/StorageEntry"
        auditStorageBytes:
          $ref: "#/components/schemas/StorageEntry"
      description: Record activity breakdown
    SearchSection:
      type: object
      properties:
        queries:
          $ref: "#/components/schemas/QueryCounts"
        ingest:
          $ref: "#/components/schemas/IngestCounts"
        indexStorageBytes:
          $ref: "#/components/schemas/StorageBytesSearch"
      description: Search activity breakdown
    StorageBytesSearch:
      type: object
      properties:
        text:
          $ref: "#/components/schemas/StorageEntry"
        semantic:
          $ref: "#/components/schemas/StorageEntry"
        hybrid:
          $ref: "#/components/schemas/StorageEntry"
        parentChunks:
          $ref: "#/components/schemas/StorageEntry"
      description: Search index storage bytes and estimated credits by index type
    StorageEntry:
      type: object
      properties:
        bytes:
          type: integer
          description: Bytes stored
          format: int64
          example: 1024000
        estimatedCredits:
          type: integer
          description: Estimated credits this period for this storage (final billing
            at cycle end)
          format: int64
          example: 10
        estimatedCreditsMilli:
          type: integer
          description: "estimatedCredits in milli-credit precision (exact). Storage\
            \ credits are computed in whole-credit units today (HUNDRED_MB granularity),\
            \ so this currently equals estimatedCredits * 1000 — the field is exposed\
            \ for future-proofing when sub-credit storage rates land."
          format: int64
          example: 10000
      description: Storage bytes with estimated monthly credit cost
    TenantCredits:
      type: object
      properties:
        used:
          type: integer
          description: Credits consumed by this environment this period (rounded down)
          format: int64
          example: 8200
        usedMilli:
          type: integer
          description: "used in milli-credit precision (exact, 1 credit = 1000 milli-credits).\
            \ Use this for exact accounting — `tenants.live.credits.used + tenants.test.credits.used`\
            \ can differ from the account-level `credits.used` by up to 1 credit due\
            \ to independent truncation, but `live.usedMilli + test.usedMilli == credits.usedMilli`\
            \ always."
          format: int64
          example: 8200000
      description: Environment-scoped credit total (no limit/breakdown — those live
        at the account level)
    TenantDetail:
      type: object
      properties:
        id:
          type: string
          description: "Environment id — your account (tenant) id for this environment,\
            \ as a UUID. Null if the environment doesn't exist (rare)."
          nullable: true
        credits:
          $ref: "#/components/schemas/TenantCredits"
        search:
          $ref: "#/components/schemas/SearchSection"
        documents:
          $ref: "#/components/schemas/DocumentsSection"
        records:
          $ref: "#/components/schemas/RecordsSection"
        inference:
          $ref: "#/components/schemas/TenantInferenceSection"
      description: Full usage breakdown for a single environment
    TenantInferenceSection:
      type: object
      properties:
        endpoints:
          $ref: "#/components/schemas/InferenceEndpoints"
        totals:
          $ref: "#/components/schemas/InferenceCallStats"
      description: Per-environment inference breakdown (no pre-paid balance — that's
        an account-level ledger)
    Tenants:
      type: object
      properties:
        live:
          $ref: "#/components/schemas/TenantDetail"
        test:
          $ref: "#/components/schemas/TenantDetail"
      description: Usage breakdown by environment (your live and test environments).
        Your account totals at the top of the response equal the sum of live + test
        for each metric.
    UsageReportResponse:
      type: object
      properties:
        period:
          type: string
          description: Billing period in YYYY-MM format
          example: 2026-05
        credits:
          $ref: "#/components/schemas/Credits"
        search:
          $ref: "#/components/schemas/SearchSection"
        documents:
          $ref: "#/components/schemas/DocumentsSection"
        records:
          $ref: "#/components/schemas/RecordsSection"
        readAccess:
          $ref: "#/components/schemas/ReadAccessSection"
        identity:
          $ref: "#/components/schemas/IdentitySection"
        inference:
          $ref: "#/components/schemas/InferenceSection"
        tenants:
          $ref: "#/components/schemas/Tenants"
        contexts:
          type: array
          description: "Per-AppContext usage breakdown. One entry per context across\
            \ your live and test environments — the finest granularity at which Vectros\
            \ usage is attributed. Additive to the environment and account rollups:\
            \ a context's metrics roll up into its environment, which rolls up into\
            \ your account totals. Filtered to a single context when the `contextId`\
            \ query param is supplied; otherwise lists every context with activity\
            \ or provisioned rows."
          items:
            $ref: "#/components/schemas/ContextDetail"
      description: "Usage report for your account across its live and test environments\
        \ for a given billing period. Returned by GET /v1/usage. Includes credit totals,\
        \ a per-category breakdown, search/document/record/identity/inference detail,\
        \ and a live/test environment decomposition."
    WriteStats:
      type: object
      properties:
        count:
          type: integer
          description: Base writes (one per create/update)
          format: int64
          example: 120
        indexCount:
          type: integer
          description: "Logical index credits maintained by these writes — ownership\
            \ + externalId + schema lookup fields (an equality index counts 1, a range-enabled\
            \ index counts 3)."
          format: int64
          example: 180
        credits:
          type: integer
          description: Credits charged for all writes on this surface this period
            (rounded down)
          format: int64
          example: 300
        creditsMilli:
          type: integer
          description: credits in milli-credit precision (exact)
          format: int64
          example: 300000
      description: "Write counts and credits for a schema-driven surface (documents\
        \ / identity). Same shape as record writes: a base count plus the extra schema-lookup-field\
        \ writes, with the combined credits."
    ClientResponse:
      type: object
      properties:
        id:
          type: string
          description: "The Vectros-assigned UUID for this client. Use this ID when\
            \ referencing the client in token minting, record ownership, and filters."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        externalId:
          type: string
          description: "Your own identifier for this client, supplied when it was\
            \ created."
          example: patient_789
        name:
          type: string
          description: Display name for this client.
          example: Jane Doe
        status:
          type: string
          description: Lifecycle status of the client. `ACTIVE` clients can be used
            normally; `SUSPENDED` clients are retained but blocked from new operations.
          example: ACTIVE
        orgId:
          type: string
          description: "The Vectros-assigned UUID of the organization this client\
            \ belongs to, or null if the client is not associated with an org."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        payload:
          type: object
          additionalProperties: true
          description: "Free-form key-value attributes stored with this client. Sensitive\
            \ fields are returned as `[redacted]` unless your token grants the sensitive-reveal\
            \ scope for the client's schema."
        schemaId:
          type: string
          description: "The ID of the schema that validates this client's payload\
            \ and drives its lookup indexing, or null if the client is not schema-bound."
          example: 550e8400-e29b-41d4-a716-446655440000
        schemaVersion:
          type: integer
          description: The version of the governing schema that was in effect when
            this client was last written. The client keeps the version it was stamped
            with even after the schema is edited. Null when the client is not schema-bound.
          format: int32
          example: 3
        createdAt:
          type: string
          description: "When the client was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
      description: A client (subject) identity that you control.
    ClientRequest:
      required:
      - externalId
      type: object
      properties:
        externalId:
          type: string
          description: "Your own unique identifier for this client. Used for idempotent\
            \ create: if a client with this `externalId` already exists, it is returned\
            \ instead of creating a duplicate."
          example: patient_789
        name:
          type: string
          description: Display name for this client.
          example: Jane Doe
        status:
          type: string
          description: Lifecycle status of the client. `ACTIVE` clients can be used
            normally; `SUSPENDED` clients are retained but blocked from new operations.
            Defaults to `ACTIVE`.
          example: ACTIVE
          enum:
          - ACTIVE
          - SUSPENDED
        orgId:
          type: string
          description: The organization this client belongs to. Must be the Vectros-assigned
            UUID of an org created via `POST /v1/orgs`; use `GET /v1/orgs?externalId=`
            to resolve your own identifier to this UUID.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        payload:
          type: object
          additionalProperties: true
          description: "Free-form key-value attributes to store with this client —\
            \ handy for keeping extra data from your system without defining a schema.\
            \ On update, the supplied object replaces the stored payload in full (it\
            \ is not key-merged); omit it to leave the payload unchanged."
          example:
            dob: 1985-03-22
            plan: premium
        schemaId:
          type: string
          description: "Optional ID of a schema (created via `POST /v1/schemas`) that\
            \ validates this client's payload and drives its lookup indexing. When\
            \ set, the schema must belong to your account, the payload is validated\
            \ against the schema's fields on save, and the schema's declared lookup\
            \ fields become queryable via `GET /v1/clients?type=...&field=...&value=...`.\
            \ When omitted, the payload is free-form and no lookup rows are written.\
            \ On update, omit this field to leave it unchanged; once set, it cannot\
            \ be cleared."
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "Request body for creating or updating a client. On update (PUT),\
        \ omitted fields are preserved — a null does not clear a field — and `payload`,\
        \ when supplied, replaces the stored payload in full rather than being deep-merged."
    ClientPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/ClientResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of clients as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    IdentityLookupRequest:
      required:
      - field
      - type
      type: object
      properties:
        type:
          type: string
          description: "The identity schema type whose lookup fields you are querying\
            \ (for example, `person_v1`)."
          example: person_v1
        field:
          type: string
          description: "Name of the lookup field declared on the schema. For a field\
            \ marked sensitive, this body variant is required (the GET variant rejects\
            \ looking up by a sensitive value)."
          example: ssn
        value:
          type: string
          description: Exact value to match. Mutually exclusive with `from`/`to` and
            `prefix`.
          example: 111-11-1111
        from:
          type: string
          description: "Inclusive lower bound for a range lookup (requires `to`; supported\
            \ only on range-enabled, non-sensitive fields). Mutually exclusive with\
            \ `value` and `prefix`."
          example: 2024-01-01
        to:
          type: string
          description: Inclusive upper bound for a range lookup (requires `from`).
          example: 2024-12-31
        prefix:
          type: string
          description: Prefix to match (supported only on range-enabled string fields).
            Mutually exclusive with `value` and `from`/`to`.
          example: team-
        startFrom:
          type: string
          description: Pagination cursor — pass the `nextCursor` returned by the previous
            page.
          example: 550e8400-e29b-41d4-a716-446655440000
        limit:
          maximum: 100
          minimum: 1
          type: integer
          description: Maximum number of results to return per page (1–100; defaults
            to 20).
          format: int32
          example: 20
          default: 20
        order:
          type: string
          description: "Sort direction for the returned results: `asc` (default) or\
            \ `desc`."
          example: asc
          default: asc
          enum:
          - asc
          - desc
      description: "Request body for an identity lookup (user, organization, or client).\
        \ Carries the lookup value in the body so a sensitive field's value never\
        \ travels in the URL query string. Provide exactly one lookup mode: `value`\
        \ (exact match), `from` plus `to` (range), or `prefix`."
    DocumentResponse:
      type: object
      properties:
        id:
          type: string
          description: The Vectros-assigned unique ID of the document.
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: The document's title.
          example: Patient Intake Form — Jane Doe
        externalId:
          type: string
          description: "Your own stable identifier for this document (immutable, and\
            \ unique within your context). Null if the document was created without\
            \ one."
          example: intake-doc-2024-0042
        status:
          type: string
          description: "Processing status of the document. PENDING_UPLOAD, UPLOADED,\
            \ EXTRACTING, and PENDING_INDEX are in-flight states; INDEXED means the\
            \ document is indexed and searchable; SKIPPED means extraction produced\
            \ no indexable text so there was nothing to index — the document is stored\
            \ and retrievable, just not searchable (not an error); STORED means it\
            \ is store-only (indexMode=NONE) — persisted and retrievable but by design\
            \ not searchable; FAILED means processing failed."
          example: INDEXED
          enum:
          - PENDING_UPLOAD
          - UPLOADED
          - EXTRACTING
          - PENDING_INDEX
          - INDEXED
          - SKIPPED
          - STORED
          - FAILED
        indexMode:
          type: string
          description: "How this document is indexed. HYBRID, SEMANTIC, and TEXT are\
            \ searchable; NONE is store-only (not indexed for search)."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        storeText:
          type: boolean
          description: "Whether the raw text is stored and retrievable via GET /v1/documents/{id}/text."
          example: true
        folderId:
          type: string
          description: The ID of the folder that contains this document.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        payload:
          type: object
          additionalProperties: true
          description: "The document's structured payload, as a JSON object. When\
            \ `schemaId` is set, schema-declared fields are validated and indexed\
            \ for lookup; undeclared keys pass through as free-form. A by-id GET returns\
            \ the full payload; on list responses this may be only the inline subset\
            \ of fields (see `payloadExternalized`)."
        payloadExternalized:
          type: boolean
          description: True when this document's structured payload is large enough
            to be stored externally rather than inline. In that case `payload` on
            a list response holds only the inline subset of fields; fetch the full
            payload with a by-id GET.
          example: false
        schemaId:
          type: string
          description: The ID of the record schema this document's `payload` is validated
            and indexed against. Null if the document is schemaless.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        schemaVersion:
          type: integer
          description: "The version of the bound schema in effect when this document\
            \ was written. A document keeps the version it was stamped with at write\
            \ time, even after the schema is later edited. Null when the document\
            \ is schemaless."
          format: int32
          example: 3
        textBytes:
          type: integer
          description: "Size of the extracted text, in bytes."
          format: int64
          example: 4096
        userId:
          type: string
          description: The owning user — the Vectros-assigned UUID of a user in your
            account.
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: The owning organization — the Vectros-assigned UUID of an organization
            in your account.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: The associated client — the Vectros-assigned UUID of a client
            in your account.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        fileType:
          type: string
          description: MIME type of the uploaded file. Present only for file-backed
            documents.
          example: application/pdf
        fileSize:
          type: integer
          description: "Size of the uploaded file, in bytes. Present only for file-backed\
            \ documents."
          format: int64
          example: 204800
        createdAt:
          type: string
          description: "When the document was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        lastModified:
          type: string
          description: "When the document was last modified, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T10:45:00Z
        version:
          type: integer
          description: Optimistic-concurrency version. Pass this back as `expectedVersion`
            on a later update to avoid silently overwriting a concurrent change —
            the update is rejected with 409 if the document has moved on. Incremented
            on every successful write.
          format: int64
          example: 5
      description: "A document, created either by ingesting raw text or by uploading\
        \ a file."
    DocumentRequest:
      required:
      - title
      type: object
      properties:
        title:
          type: string
          description: Human-readable document title
          example: Patient Intake Form — Jane Doe
        text:
          type: string
          description: "Raw text content to ingest. Required when creating a document\
            \ via the text-ingest path. On update, supply it to replace the stored\
            \ text; omit it to leave the existing text unchanged."
          example: Patient presented with symptoms of...
        indexMode:
          type: string
          description: "Indexing strategy for this document. `HYBRID` runs both BM25\
            \ keyword and dense-vector semantic indexing (recommended for most use\
            \ cases). `SEMANTIC` indexes only as dense vectors — best for conceptual\
            \ similarity search. `TEXT` indexes only with BM25 — best for exact keyword\
            \ matching. `NONE` stores the document without search indexing (store-only\
            \ / archival): it remains retrievable by id and by structured-field lookup\
            \ but never appears in search results. Optional: omit to inherit the bound\
            \ schema's default index mode. If neither this field nor the schema specifies\
            \ one, the request is rejected. When both are set, this per-document value\
            \ wins."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        storeText:
          type: boolean
          description: "If true, the raw text is retained so you can later fetch it\
            \ via `GET /v1/documents/{id}/text`. Defaults to false to minimize storage\
            \ costs."
          example: false
        folderId:
          type: string
          description: "ID of the folder to place this document in. On create, omit\
            \ to use your account's default root folder. On update, omit to leave\
            \ unchanged — this field cannot currently be cleared once set."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        payload:
          type: object
          additionalProperties: true
          description: "The document's structured data, as a flat key/value object.\
            \ When `schemaId` is set, declared fields are validated against the schema\
            \ and its lookup fields become directly queryable via `GET /v1/documents?type=&field=&value=`\
            \ (sensitive fields are blind-indexed); undeclared keys pass through as\
            \ free-form and are searchable via the `filters` parameter on `POST /v1/search`.\
            \ A strict schema rejects undeclared keys. On update, the supplied object\
            \ replaces the stored payload in full (it is not key-merged); omit to\
            \ leave it unchanged."
          example:
            patientId: P-1042
            status: active
            category: intake
        schemaId:
          type: string
          description: "Optional ID of a record schema to bind this document to. When\
            \ set, the document's `payload` is validated against the schema's fields\
            \ on save, and the schema's lookup fields become directly queryable (the\
            \ same behavior as records). On update, omit to leave unchanged."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        userId:
          type: string
          description: "Owning user ID — the Vectros-assigned UUID of a user in your\
            \ account. Optional. With an API key, sets the document's owner explicitly.\
            \ With a scoped token, must match the token's identity claim (if set)\
            \ or fall within its data scope."
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: "Owning organization ID — the Vectros-assigned UUID of an organization\
            \ in your account. Optional. With an API key, sets the document's owning\
            \ organization explicitly. With a scoped token, must match the token's\
            \ identity claim (if set) or fall within its data scope."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: "Associated client ID — the Vectros-assigned UUID of a client\
            \ in your account. Optional. With an API key, sets the document's client\
            \ explicitly. With a scoped token, must match the token's identity claim\
            \ (if set) or fall within its data scope."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        externalId:
          type: string
          description: "Stable, caller-supplied identifier for this document. Optional.\
            \ Immutable after create. Unique within your account and context: posting\
            \ again with the same `externalId` returns the existing document (idempotent\
            \ ingest), and it is the key other records use to reference this one.\
            \ Max 256 characters."
          example: intake-doc-2024-0042
        expectedVersion:
          type: integer
          description: "Optimistic-concurrency token. Pass the `version` you last\
            \ read (from a GET or a prior write response) to make this update conditional\
            \ — it is rejected with `409 VERSION_CONFLICT` if the document was modified\
            \ since, leaving the stored document untouched. Omit for last-write-wins\
            \ (the default). Ignored on create."
          format: int64
          example: 5
      description: "Request body for creating or updating a text-ingested document.\
        \ Update semantics (PUT): omitted fields are preserved — sending null does\
        \ not clear a field; `payload` is replaced in full when supplied (not deep-merged);\
        \ `indexMode` is fixed at creation and ignored on update; `text` is used only\
        \ on the POST ingest path."
    DocumentLookupPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/DocumentResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of documents matched by a structured-field lookup, as a\
        \ `{data, nextCursor}` envelope. `nextCursor` is an opaque cursor — pass it\
        \ back as `startFrom` to fetch the next page, or null when no more pages remain."
    DocumentLookupRequest:
      required:
      - field
      - type
      type: object
      properties:
        type:
          type: string
          description: Document type to look up (the type defined by the bound schema).
          example: invoice
        field:
          type: string
          description: "Name of a lookup field declared on the schema. For a sensitive\
            \ field, this body variant is required (the GET variant rejects sensitive-field\
            \ lookups)."
          example: mrn
        value:
          type: string
          description: Exact value to match (equality mode). Mutually exclusive with
            `from`/`to` and `prefix`.
          example: MRN-1001
        from:
          type: string
          description: "Inclusive lower bound for a range lookup (requires `to`; non-sensitive,\
            \ range-enabled fields only). Mutually exclusive with `value` and `prefix`."
          example: 2024-01-01
        to:
          type: string
          description: Inclusive upper bound for a range lookup (requires `from`;
            mutually exclusive with `value` and `prefix`).
          example: 2024-12-31
        prefix:
          type: string
          description: Prefix to match for a prefix lookup (range-enabled string fields
            only). Mutually exclusive with `value` and `from`/`to`.
          example: PO-2024
        startFrom:
          type: string
          description: Pagination cursor. Pass the `nextCursor` from the previous
            page to fetch the next page; omit it for the first page.
          example: 550e8400-e29b-41d4-a716-446655440000
        limit:
          maximum: 100
          minimum: 1
          type: integer
          description: Maximum number of documents to return per page (1–100; defaults
            to 20).
          format: int32
          example: 20
          default: 20
        order:
          type: string
          description: "Sort direction for the returned results: `asc` (default) or\
            \ `desc`."
          example: asc
          default: asc
          enum:
          - asc
          - desc
      description: Request body for `POST /v1/documents/lookup`. Carries the lookup
        value in the body so a sensitive field's value never travels in the URL query
        string.
    DocumentDownloadResponse:
      type: object
      properties:
        id:
          type: string
          description: Document ID.
          example: 550e8400-e29b-41d4-a716-446655440000
        downloadUrl:
          type: string
          description: Presigned download URL for the original file. Short-lived —
            fetch the file before the time given in `expires`.
          example: https://files.example.com/uploads/...?X-Amz-Signature=...
        expires:
          type: integer
          description: Epoch-millis (Unix milliseconds) timestamp at which the presigned
            URL expires.
          format: int64
          example: 1779999999999
        fileType:
          type: string
          description: "MIME type of the file, as captured at upload time."
          example: application/pdf
      description: Presigned download URL for a file-backed document.
    DocumentTextResponse:
      type: object
      properties:
        id:
          type: string
          description: Document ID.
          example: 550e8400-e29b-41d4-a716-446655440000
        text:
          type: string
          description: Full extracted or ingested text content of the document.
          example: Patient presents with hypertension and elevated cholesterol levels.
      description: The raw stored text body of a document.
    DocumentPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/DocumentResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of documents as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    FileUploadResponse:
      type: object
      properties:
        id:
          type: string
          description: "Document ID assigned to this upload. Poll `GET /v1/documents/{id}`\
            \ to track indexing status after the upload completes."
          example: 550e8400-e29b-41d4-a716-446655440000
        uploadUrl:
          type: string
          description: "Presigned upload URL. Upload the raw file bytes directly to\
            \ this URL with an HTTP PUT request, setting the Content-Type header to\
            \ the `fileType` you provided. No Authorization header is needed — the\
            \ URL is self-authenticating."
          example: https://bucket.s3.amazonaws.com/folder/file?X-Amz-Signature=...
        expiresAt:
          type: string
          description: ISO-8601 UTC timestamp at which the presigned URL expires.
            The upload must complete before this time. Default expiry is 60 minutes.
          example: 2024-01-15T10:30:00Z
        externalId:
          type: string
          description: "The stable identifier you supplied on the request, echoed\
            \ back. Immutable, and unique within your account and context. Null when\
            \ you did not supply one."
          example: intake-doc-2024-0042
      description: Response containing the presigned upload URL and the assigned document
        ID.
    FileUploadRequest:
      required:
      - fileName
      - fileType
      type: object
      properties:
        fileName:
          type: string
          description: "Original file name, including its extension. Used as the document\
            \ title when no separate title is provided."
          example: patient_intake_2024_01_15.pdf
        fileType:
          type: string
          description: MIME type of the file being uploaded. Used to set the correct
            Content-Type on the stored object.
          example: application/pdf
        indexMode:
          type: string
          description: "Indexing strategy applied after the file is processed and\
            \ its text is extracted. `HYBRID` runs both BM25 keyword and dense-vector\
            \ semantic indexing (recommended). `SEMANTIC` indexes only as dense vectors.\
            \ `TEXT` indexes only with BM25. `NONE` is store-only (archival): the\
            \ file is still uploaded and its text extracted, but it is not search-indexed\
            \ — retrievable by id/download and structured-field lookup only. Optional:\
            \ omit to inherit the bound schema's default index mode. If neither this\
            \ field nor the schema specifies one, the request is rejected. When both\
            \ are set, this per-file value wins."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        folderId:
          type: string
          description: ID of the folder in which to place this document. Omit to use
            your account's default root folder.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        payload:
          type: object
          additionalProperties: true
          description: "The document's structured data, as a flat key/value object.\
            \ When `schemaId` is set, declared fields are validated against the schema\
            \ and its lookup fields become directly queryable (matching record and\
            \ text-ingest behavior); undeclared keys pass through as free-form and\
            \ are searchable via the `filters` parameter on `POST /v1/search`."
          example:
            patientId: P-1042
            status: active
            category: clinical
        schemaId:
          type: string
          description: "Optional ID of a record schema to bind this document to. When\
            \ set, the document's `payload` is validated against the schema and its\
            \ lookup fields become directly queryable (matching record and text-ingest\
            \ behavior)."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        userId:
          type: string
          description: "Owning user ID — the Vectros-assigned UUID of a user in your\
            \ account. Optional. With an API key, sets the document's owner explicitly.\
            \ With a scoped token, must match the token's identity claim (if set)\
            \ or fall within its data scope."
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: "Owning organization ID — the Vectros-assigned UUID of an organization\
            \ in your account. Optional. With an API key, sets the document's owning\
            \ organization explicitly. With a scoped token, must match the token's\
            \ identity claim (if set) or fall within its data scope."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: "Associated client ID — the Vectros-assigned UUID of a client\
            \ in your account. Optional. With an API key, sets the document's client\
            \ explicitly. With a scoped token, must match the token's identity claim\
            \ (if set) or fall within its data scope."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        externalId:
          type: string
          description: "Stable, caller-supplied identifier for this document. Optional.\
            \ Immutable after create. Unique within your account and context: initiating\
            \ an upload again with the same `externalId` returns the same document\
            \ plus a fresh presigned URL (idempotent — no duplicate), and it is the\
            \ key other records use to reference this one. Max 256 characters."
          example: intake-doc-2024-0042
      description: Request body for initiating a file upload via a presigned S3 URL.
    ErasureCertificate:
      type: object
      properties:
        contextsSwept:
          type: array
          description: "The contexts that were actually swept. When you omit or leave\
            \ the request's context scope empty, this lists every context the subject\
            \ had data in. Any context not listed here was not erased."
          items:
            type: string
            example: ctx_marketing
        perContext:
          type: array
          description: "Deletion counts broken down per context, one entry for each\
            \ context that was swept."
          items:
            $ref: "#/components/schemas/ErasureContextResult"
        danglingReferences:
          type: integer
          description: Number of reference fields within your account that point at
            the erased subject and were left dangling. These are reported for your
            own diagnosis and are never automatically cascaded into another subject's
            records.
          format: int64
          example: 0
        sharedRowsSkipped:
          type: integer
          description: Number of shared or ownerless rows that the subject did not
            solely own. These are reported but not erased — erasure only removes rows
            the subject solely owns.
          format: int64
          example: 0
        auditDisposition:
          type: string
          description: "The audit-trail disposition that was applied, echoing the\
            \ request."
          example: retain-redacted
          enum:
          - retain-redacted
          - purge
      description: "Verifiable proof of an erasure: which contexts were swept, the\
        \ per-context deletion counts, and reports of references and shared rows that\
        \ were detected but deliberately left intact rather than cascaded."
    ErasureContextResult:
      type: object
      properties:
        contextId:
          type: string
          description: The context that was erased.
          example: ctx_marketing
        recordsDeleted:
          type: integer
          description: Number of records deleted in this context.
          format: int64
          example: 42
        documentsDeleted:
          type: integer
          description: Number of documents deleted in this context.
          format: int64
          example: 7
        foldersDeleted:
          type: integer
          description: Number of folders deleted in this context.
          format: int64
          example: 3
        identityRowsDeleted:
          type: integer
          description: "Number of identity rows deleted (the subject's user, organization,\
            \ or client record plus its lookup rows). Because the subject's identity\
            \ is account-wide rather than per-context, it is counted only once here\
            \ — against the context in which it was resolved."
          format: int64
          example: 1
      description: Deletion counts for a single erased context.
    ErasureRequestResponse:
      type: object
      properties:
        requestId:
          type: string
          description: "Opaque identifier for the erasure request. Pass this to `GET\
            \ /v1/erasure-requests/{id}` to poll for status and, once complete, the\
            \ certificate."
          example: er_550e8400e29b41d4a716446655440000
        status:
          type: string
          description: "Current status of the job. `accepted` immediately after submission,\
            \ then `processing` while the erasure runs, then either `completed` (the\
            \ certificate is populated) or `failed` (see `failureReason`)."
          example: accepted
          enum:
          - accepted
          - processing
          - completed
          - failed
        subjectType:
          type: string
          description: "The kind of subject that was erased (`user`, `client`, or\
            \ `org`), echoed back from your request."
          example: user
          enum:
          - user
          - client
          - org
        subjectId:
          type: string
          description: "The resolved platform id of the erased subject, echoed back\
            \ once it has been resolved."
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: "When the request was accepted, as an ISO-8601 UTC timestamp."
          example: 2026-06-12T09:00:00Z
        completedAt:
          type: string
          description: "When the erasure finished, as an ISO-8601 UTC timestamp. Null\
            \ until `status` is `completed`."
          example: 2026-06-12T09:00:42Z
        failureReason:
          type: string
          description: "Reason the erasure failed, suitable for display or programmatic\
            \ handling so you do not have to inspect logs. Present only when `status`\
            \ is `failed`; null otherwise."
          example: context_enumeration_error
        certificate:
          $ref: "#/components/schemas/ErasureCertificate"
      description: Job handle and result for an asynchronous erasure request. While
        the job runs this carries its status; once it completes it also carries a
        certificate that is the verifiable record of exactly what was erased.
    ErasureRequest:
      required:
      - subjectType
      type: object
      properties:
        subjectType:
          type: string
          description: "The kind of end-subject to erase: `user`, `client`, or `org`."
          example: user
          enum:
          - user
          - client
          - org
        subjectId:
          type: string
          description: "The platform id of the subject to erase (the userId, clientId,\
            \ or orgId returned when the subject was created). Provide exactly one\
            \ of `subjectId` or `externalId`."
          example: 550e8400-e29b-41d4-a716-446655440000
        externalId:
          type: string
          description: "Your own externalId for the subject, as an alternative to\
            \ `subjectId`. Provide exactly one of `subjectId` or `externalId`."
          example: patient-12345
        contextScope:
          type: array
          description: "The contexts in which to erase the subject. As the data controller,\
            \ you declare the blast radius: each listed context is erased independently\
            \ and reported separately on the certificate. Omit or leave empty to erase\
            \ every context the subject has data in; those contexts are then enumerated\
            \ and erased one at a time, never as a single account-wide sweep. Any\
            \ context you do not list is left untouched, and managing that residual\
            \ data is your responsibility."
          items:
            type: string
            example: ctx_marketing
        auditDisposition:
          type: string
          description: How to handle the subject's audit and version-history trail.
            This governs only the audit data — the subject's records and documents
            are always erased regardless. `retain-redacted` (the default) keeps the
            compliance audit trail with sensitive data redacted; `purge` additionally
            hard-removes the audit history itself (subject to your legal obligations).
          example: retain-redacted
          default: retain-redacted
          enum:
          - retain-redacted
          - purge
      description: Request body for an end-subject right-to-erasure request. Requires
        a root API key.
    ExportManifest:
      type: object
      properties:
        formatVersion:
          type: string
          description: "Schema version of the manifest and payload, for forward-compatible\
            \ parsing. It is bumped whenever the exported row schema changes, so branch\
            \ your parsing logic on this value."
          example: "1.0"
        format:
          type: string
          description: "Serialization format of the payload. NDJSON (newline-delimited\
            \ JSON, one row per line) is currently the only supported format."
          example: ndjson
          enum:
          - ndjson
        sizeBytes:
          type: integer
          description: "Total uncompressed size of the export payload, in bytes."
          format: int64
          example: 1048576
        contextsExported:
          type: array
          description: The context ids included in this export.
          items:
            type: string
            example: ctx_marketing
        totalRecords:
          type: integer
          description: Total number of records included across all exported contexts.
          format: int64
          example: 1280
        totalDocuments:
          type: integer
          description: Total number of documents included across all exported contexts.
          format: int64
          example: 312
        totalFolders:
          type: integer
          description: Total number of folders included across all exported contexts.
          format: int64
          example: 44
        totalIdentities:
          type: integer
          description: "Total number of identity rows included (users, organizations,\
            \ and clients)."
          format: int64
          example: 97
      description: "Describes a completed export: the payload's serialization format\
        \ and version, plus a per-context summary of what it contains."
    ExportRequestResponse:
      type: object
      properties:
        exportJobId:
          type: string
          description: "Opaque identifier for the export job. Pass this to `GET /v1/admin/export/{id}`\
            \ to poll for status and, once complete, the download URL."
          example: exp_550e8400e29b41d4a716446655440000
        status:
          type: string
          description: "Current status of the job. `accepted` immediately after submission,\
            \ then `processing` while the export runs, then either `completed` (the\
            \ download URL and manifest are populated) or `failed` (see `failureReason`)."
          example: accepted
          enum:
          - accepted
          - processing
          - completed
          - failed
        scope:
          type: string
          description: "Echoes the scope you requested: `tenant` for a full export\
            \ of your account, or `subject` for a single end-subject's data."
          example: tenant
          enum:
          - tenant
          - subject
        subjectType:
          type: string
          description: "For a `subject`-scoped export, the kind of subject that was\
            \ exported (echoed back). Null for a `tenant`-scoped export."
          example: user
          enum:
          - user
          - client
          - org
        subjectId:
          type: string
          description: "For a `subject`-scoped export, the resolved platform id of\
            \ the exported subject (echoed back). Null for a `tenant`-scoped export."
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: "When the job was accepted, as an ISO-8601 UTC timestamp."
          example: 2026-06-12T09:00:00Z
        completedAt:
          type: string
          description: "When the export finished, as an ISO-8601 UTC timestamp. Null\
            \ until `status` is `completed`."
          example: 2026-06-12T09:03:20Z
        downloadUrl:
          type: string
          description: Short-lived presigned URL for downloading the serialized export
            payload. Present once `status` is `completed`; null while the job is accepted
            or processing. Treat it as a secret — anyone with the URL can download
            the data until it expires.
          example: https://example-export-bucket.s3.amazonaws.com/exports/exp_...?X-Amz-Signature=...
        expiresAt:
          type: string
          description: "When the presigned download URL expires, as an ISO-8601 UTC\
            \ timestamp. Poll the job again after this time to obtain a fresh URL.\
            \ Null until `status` is `completed`."
          example: 2026-06-12T10:03:20Z
        failureReason:
          type: string
          description: "Reason the export failed, suitable for display or programmatic\
            \ handling. Present only when `status` is `failed`; null otherwise."
          example: size_limit_exceeded
        manifest:
          $ref: "#/components/schemas/ExportManifest"
      description: Job handle and result for an asynchronous data-export request.
        While the job runs this carries its status; once it completes it also carries
        a short-lived presigned download URL and a manifest describing the exported
        payload.
    ExportRequest:
      type: object
      properties:
        scope:
          type: string
          description: "What to export. `tenant` (the default) exports all of your\
            \ account's data across the requested contexts; `subject` exports a single\
            \ end-subject's data (for example, to satisfy a one-person data-portability\
            \ request). Additional scopes may be added in the future."
          example: tenant
          default: tenant
          enum:
          - tenant
          - subject
        subjectType:
          type: string
          description: "The kind of subject to export: `user`, `client`, or `org`.\
            \ Required only when `scope` is `subject`."
          example: user
          enum:
          - user
          - client
          - org
        subjectId:
          type: string
          description: The platform id of the subject to export. Used only when `scope`
            is `subject`. Provide exactly one of `subjectId` or `externalId`.
          example: 550e8400-e29b-41d4-a716-446655440000
        externalId:
          type: string
          description: "Your own externalId for the subject, as an alternative to\
            \ `subjectId` (only when `scope` is `subject`). Provide exactly one of\
            \ `subjectId` or `externalId`."
          example: patient-12345
        contextScope:
          type: array
          description: The contexts to export. Omit or leave empty to export all of
            your contexts. Each listed context is exported and reported separately
            in the manifest.
          items:
            type: string
            example: ctx_marketing
        format:
          type: string
          description: "Serialization format of the export payload. NDJSON (newline-delimited\
            \ JSON, one row per line) is currently the only supported format; more\
            \ may be added in the future. The manifest carries a `formatVersion` so\
            \ you can parse the payload in a forward-compatible way."
          example: ndjson
          default: ndjson
          enum:
          - ndjson
        includeAuditHistory:
          type: boolean
          description: "Whether to also include the audit and version-history trail\
            \ of the exported data, not just the current live rows. Defaults to false\
            \ (live data only). Set true to also export the retained version history.\
            \ This governs only the audit data — the live records are always included."
          example: false
          default: false
      description: Request body for submitting a data-export job. Requires a root
        API key.
    FolderResponse:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier for the folder, assigned by Vectros."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        name:
          type: string
          description: Human-readable display name of the folder.
          example: Patient Records 2024
        description:
          type: string
          description: Optional description of the folder's contents or purpose.
          example: All intake forms for Q1 2024
        parentFolderId:
          type: string
          description: ID of this folder's parent. Null when the folder is a root
            folder.
          example: 550e8400-e29b-41d4-a716-446655440000
        slug:
          type: string
          description: "Stable URL-safe slug for the folder, unique among its siblings\
            \ under the same parent. Assigned at creation and immutable thereafter."
          example: intake-forms
        depth:
          type: integer
          description: How deeply the folder is nested. A root folder has depth 0;
            each level below increments by 1.
          format: int32
          example: 1
        isProtected:
          type: boolean
          description: Whether this folder is protected from deletion. Your context's
            root folder is protected.
          example: false
        userId:
          type: string
          description: ID of the user that owns this folder — the Vectros-assigned
            UUID of a user in your account. Null if the folder is not owned by a specific
            user.
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: ID of the organization that owns this folder — the Vectros-assigned
            UUID of an organization in your account. Null if the folder is not owned
            by a specific organization.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: ID of the client associated with this folder — the Vectros-assigned
            UUID of a client in your account. Null if the folder is not associated
            with a specific client.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        createdAt:
          type: string
          description: "When the folder was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        lastModified:
          type: string
          description: "When the folder was last modified, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T10:45:00Z
        version:
          type: integer
          description: "Optimistic-concurrency version, incremented on every successful\
            \ write. Pass it back as `expectedVersion` on a later update or patch\
            \ to avoid silently overwriting a concurrent change — the write is rejected\
            \ with a 409 version conflict if the folder has changed since."
          format: int64
          example: 5
      description: A folder used to organize your documents and records.
    FolderRequest:
      required:
      - name
      type: object
      properties:
        name:
          type: string
          description: Human-readable display name of the folder. Required.
          example: Patient Records 2024
        description:
          type: string
          description: Optional description of the folder's contents or purpose.
          example: All intake forms and clinical notes for Q1 2024
        parentFolderId:
          type: string
          description: "ID of the parent folder to create this folder under, applied\
            \ at creation only. Omit to create the folder under your context's default\
            \ root folder. Ignored on update: a folder cannot currently be moved via\
            \ the API."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        slug:
          type: string
          description: "Optional stable slug for the folder, unique among its siblings\
            \ under the same parent. Useful as an idempotency key so re-creating the\
            \ same folder returns the existing one rather than a duplicate. Derived\
            \ from the name when omitted. Must be lowercase letters, digits, and hyphens.\
            \ Immutable once the folder is created."
          example: intake-forms
        userId:
          type: string
          description: "Optional ID of the user that should own this folder — the\
            \ Vectros-assigned UUID of a user in your account. With an API key, this\
            \ sets the folder's owner directly. With a scoped token, it must match\
            \ the token's identity claim (if one is set) or fall within the token's\
            \ data scope."
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: "Optional ID of the organization that should own this folder\
            \ — the Vectros-assigned UUID of an organization in your account. With\
            \ an API key, this sets the owning organization directly. With a scoped\
            \ token, it must match the token's identity claim (if one is set) or fall\
            \ within the token's data scope."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: "Optional ID of the client to associate with this folder —\
            \ the Vectros-assigned UUID of a client in your account. With an API key,\
            \ this sets the client directly. With a scoped token, it must match the\
            \ token's identity claim (if one is set) or fall within the token's data\
            \ scope."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        expectedVersion:
          type: integer
          description: Optimistic-concurrency token for updates. Pass the `version`
            you last read to make the update conditional — it is rejected with a 409
            version conflict (and the stored folder is left untouched) if the folder
            changed since. Omit for last-write-wins (the default). Ignored on create.
          format: int64
          example: 5
      description: "Request body for creating or updating a folder. On update (PUT\
        \ or PATCH), omitted fields are preserved — a null does not clear a field.\
        \ `parentFolderId` is honored only at creation and ignored on update, because\
        \ a folder cannot currently be moved via the API."
    FolderPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/FolderResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of folders, returned as a `{data, nextCursor}` envelope.\
        \ `nextCursor` is an opaque cursor you pass as `startFrom` to fetch the next\
        \ page; it is null when no more pages remain."
    ModelInfo:
      required:
      - availableOn
      - contextWindow
      - id
      - inputCreditsPer1kTokens
      - name
      - outputCreditsPer1kTokens
      - provider
      - regionPricing
      type: object
      properties:
        id:
          type: string
          description: Stable model alias. Pass this as the `model` field on `/v1/chat`
            and other inference endpoints.
          example: claude-haiku-4-5
        name:
          type: string
          description: Human-readable display name for the model.
          example: Claude Haiku 4.5
        provider:
          type: string
          description: "Name of the model vendor (for example, Anthropic or AWS),\
            \ suitable for display in a model picker."
          example: Anthropic
        contextWindow:
          type: integer
          description: "Maximum context window, in tokens."
          format: int32
          example: 200000
        availableOn:
          type: array
          description: "Plan tiers on which this model is available (a subset of:\
            \ free, starter, pro, scale, enterprise)."
          items:
            type: string
            description: "Plan tiers on which this model is available (a subset of:\
              \ free, starter, pro, scale, enterprise)."
        inputCreditsPer1kTokens:
          type: number
          description: Base (non-US) credit rate per 1K input tokens; 1 credit = 1
            cent. Requests are served from a US region by default and billed `regionPricing.us`
            (this value multiplied by `regionPremiumFactor`). See `regionPricing`
            for both the US and base rates.
          format: double
          example: 0.1
        outputCreditsPer1kTokens:
          type: number
          description: Base (non-US) credit rate per 1K output tokens; 1 credit =
            1 cent. The default US-served rate is this value multiplied by `regionPremiumFactor`;
            see `regionPricing` for both rates.
          format: double
          example: 0.5
        regionPricing:
          $ref: "#/components/schemas/RegionPricing"
      description: The available inference models.
    ModelsResponse:
      required:
      - defaultModel
      - models
      type: object
      properties:
        models:
          type: array
          description: The available inference models.
          items:
            $ref: "#/components/schemas/ModelInfo"
        defaultModel:
          type: string
          description: Model alias used when a request omits the `model` field.
          example: claude-haiku-4-5
      description: The list of available inference models plus the default model alias.
    RegionPricing:
      required:
      - base
      - globalAvailable
      - regionPremiumFactor
      - us
      type: object
      properties:
        base:
          $ref: "#/components/schemas/RegionRates"
        us:
          $ref: "#/components/schemas/RegionRates"
        regionPremiumFactor:
          type: number
          description: Premium multiplier applied to the US rate (1.0 or greater).
            A value of 1.0 means the US and base rates are equal — no premium.
          format: double
          example: 1.1
        globalAvailable:
          type: boolean
          description: Whether this model can be served from a non-US region when
            you opt into global data residency.
          example: true
      description: Per-region credit rates (per 1K tokens) for a model.
    RegionRates:
      required:
      - input
      - output
      type: object
      properties:
        input:
          type: number
          description: Credits charged per 1K input tokens.
          format: double
          example: 0.1
        output:
          type: number
          description: Credits charged per 1K output tokens.
          format: double
          example: 0.5
      description: Credit rates (per 1K input and output tokens) for a single region.
    OrgResponse:
      type: object
      properties:
        id:
          type: string
          description: The Vectros-assigned ID (UUID) for this organization. Use it
            wherever you reference the organization elsewhere — for example as the
            owner of a record or as a filter value when listing records.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        externalId:
          type: string
          description: "Your own identifier for this organization, as supplied when\
            \ it was created."
          example: clinic_001
        name:
          type: string
          description: Human-readable name of the organization.
          example: Downtown Clinic
        status:
          type: string
          description: "Lifecycle status of the organization: `ACTIVE` (usable normally)\
            \ or `SUSPENDED` (retained but blocked from new operations)."
          example: ACTIVE
        payload:
          type: object
          additionalProperties: true
          description: Free-form key-value attributes stored with the organization.
        schemaId:
          type: string
          description: "ID of the record schema that governs payload validation and\
            \ lookup indexing for this organization, or null if the organization is\
            \ not bound to a schema."
          example: 550e8400-e29b-41d4-a716-446655440000
        schemaVersion:
          type: integer
          description: The version of the governing schema in effect when this organization
            was last written. The organization keeps the schema version it was stamped
            with even after the schema is later edited. Null when the organization
            is not bound to a schema.
          format: int32
          example: 3
        createdAt:
          type: string
          description: "Timestamp when the organization was created, as an ISO-8601\
            \ UTC timestamp."
          example: 2024-01-15T09:30:00Z
      description: An organization in your account — an organizational unit you define
        and control.
    OrgRequest:
      required:
      - externalId
      type: object
      properties:
        externalId:
          type: string
          description: "Your own unique identifier for this organization. Used for\
            \ idempotent upsert: if an organization with this `externalId` already\
            \ exists, it is returned instead of creating a duplicate."
          example: clinic_001
        name:
          type: string
          description: Human-readable name for the organization.
          example: Downtown Clinic
        status:
          type: string
          description: Lifecycle status of the organization. `ACTIVE` organizations
            can be used normally; `SUSPENDED` organizations are retained but blocked
            from new operations.
          example: ACTIVE
          enum:
          - ACTIVE
          - SUSPENDED
        payload:
          type: object
          additionalProperties: true
          description: "Free-form key-value attributes to store with the organization\
            \ — use it to attach additional data from your own system. On update,\
            \ the supplied object replaces the stored payload in full (it is not key-merged);\
            \ omit it to leave the stored payload unchanged."
          example:
            region: northeast
            tier: enterprise
        schemaId:
          type: string
          description: "Optional ID of a record schema (created via `POST /v1/schemas`)\
            \ that governs payload validation and lookup indexing for this organization,\
            \ and must belong to your account. When set, the payload is validated\
            \ against the schema on save and the schema's declared lookup fields become\
            \ queryable via `GET /v1/orgs?type=...&field=...&value=...`; when omitted,\
            \ the payload is free-form and no lookup fields are indexed. Once set,\
            \ this reference cannot currently be cleared."
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "Request body for creating or updating an organization. On update\
        \ (PUT), omitted fields are preserved — a null value does not clear a field\
        \ — and the `payload` object is replaced in full when supplied, rather than\
        \ deep-merged."
    OrgPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/OrgResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of organizations as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    DataScope:
      type: object
      properties:
        userId:
          type: string
          description: Bound user id. The credential can only access records owned
            by this user.
          example: user_456
        orgId:
          type: string
          description: Bound org id. The credential can only access records owned
            by this org.
          example: org_789
      description: "Identity binding for a scoped key. `userId`, `orgId`, or both\
        \ may be present."
    PingResponse:
      required:
      - environment
      - principalKeyId
      - principalType
      - status
      - tenantId
      type: object
      properties:
        status:
          type: string
          description: "Always \"ok\" on a successful (2xx) response. Failures are\
            \ returned as a non-2xx status, so this value is never anything but \"\
            ok\"."
          example: ok
        tenantId:
          type: string
          description: Identifier of the account this credential authenticates under.
          example: tenant_abc123
        environment:
          type: string
          description: The environment that served this response.
          example: staging
          enum:
          - staging
          - production
        principalType:
          type: string
          description: "The kind of credential you authenticated with: `root_key`\
            \ = an unscoped `sk_*` API key; `scoped_key` = an `ssk_*` API key with\
            \ a bound access profile; `token` = a short-lived `st_*` token (its expiry\
            \ is in `tokenExpiresAt`)."
          example: scoped_key
          enum:
          - root_key
          - scoped_key
          - token
        principalKeyId:
          type: string
          description: Stable identifier for your credential. For `sk_*` and `ssk_*`
            keys this is the key id; for `st_*` tokens it is the token's JWT id (jti).
          example: ssk_live_xyz
        principalLabel:
          type: string
          description: "The label you set on the originating `sk_*` or `ssk_*` key.\
            \ Omitted when no label is set, or when the credential is an `st_*` token."
          example: Claude Desktop — clinical-notes RO
        allowedActions:
          type: array
          description: The actions this credential is allowed to perform. Present
            only when `principalType` is `scoped_key`.
          example:
          - search:read
          - records:read
          - documents:read
          items:
            type: string
            description: The actions this credential is allowed to perform. Present
              only when `principalType` is `scoped_key`.
            example: "[\"search:read\",\"records:read\",\"documents:read\"]"
        dataScope:
          $ref: "#/components/schemas/DataScope"
        tokenExpiresAt:
          type: integer
          description: "When the token expires, as seconds since the Unix epoch (mirrors\
            \ the JWT `exp` claim). Present only when `principalType` is `token`."
          format: int64
          example: 1733971200
      description: Authenticated health-check response carrying the identity of your
        credential. Required fields are always present; conditional fields appear
        only for the matching `principalType`.
    BatchGetResponse:
      type: object
      properties:
        data:
          type: array
          description: "The records you can access, in no guaranteed order. Any requested\
            \ id not present here was not accessible to you."
          items:
            $ref: "#/components/schemas/RecordResponse"
      description: "The subset of requested records you can access. Ids you cannot\
        \ access are silently omitted rather than reported as errors, so a missing\
        \ id never reveals whether that record exists. Reserved for a future release:\
        \ the endpoint currently returns HTTP 501 (Not Implemented)."
    RecordResponse:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier for this record (UUID), assigned by Vectros\
            \ at creation."
          example: 550e8400-e29b-41d4-a716-446655440000
        typeName:
          type: string
          description: "The record's type, matching the `typeName` declared on the\
            \ schema this record was created against."
          example: intake_form
        schemaId:
          type: string
          description: Identifier of the schema this record was validated against.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        schemaVersion:
          type: integer
          description: "The version of the governing schema at the moment this record\
            \ was written. The schema version increments on every schema edit, but\
            \ a record keeps the version it was stamped with at write time even after\
            \ the schema evolves — so you always know which schema shape the payload\
            \ conforms to. Null for a record created without a schema."
          format: int32
          example: 3
        externalId:
          type: string
          description: "Your own stable identifier for this record. Immutable, and\
            \ unique within your account, context, and record type. Null if the record\
            \ was created without one."
          example: intake-2024-0042
        payload:
          type: object
          additionalProperties: true
          description: "The record's data payload. Its structure is defined by the\
            \ referenced schema. Fields the schema marks as searchable are indexed\
            \ for full-text and semantic search. When a record's payload is stored\
            \ externally (because it is large), list and lookup responses return only\
            \ the indexed/metadata projection here and set `payloadExternalized` to\
            \ true; to retrieve the full payload, fetch the record by id (`GET /v1/records/{id}`)\
            \ or pass `includePayload=true` on the list or lookup call."
        payloadExternalized:
          type: boolean
          description: True when this record's full payload is stored externally rather
            than inline. In that case list and lookup responses return only the indexed
            projection in `payload` unless `includePayload=true` was passed; fetching
            the record by id always returns the full payload. Null (omitted) for inline
            records.
          example: true
        payloadBytes:
          type: integer
          description: Size in bytes of the full externalized payload. Present only
            when `payloadExternalized` is true.
          format: int64
          example: 20480
        status:
          type: string
          description: Record lifecycle status. ACTIVE records are live. Use this
            field to model soft-delete or workflow states without physically deleting
            records.
          example: ACTIVE
        folderId:
          type: string
          description: Identifier of the folder grouping this record. A folder can
            group both records and documents together.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        userId:
          type: string
          description: Identifier of the owning user (a Vectros-assigned UUID). Set
            automatically from the calling token's identity when the token carries
            a user identity.
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: Identifier of the owning organization (a Vectros-assigned UUID).
            Set automatically from the calling token's identity when the token carries
            an organization identity.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: Identifier of the associated client (a Vectros-assigned UUID).
            Set automatically from the calling token's identity when the token carries
            a client identity.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        indexStatus:
          type: string
          description: "Search-index status. PENDING_INDEX means the record is queued\
            \ for indexing. INDEXED means it is searchable via `POST /v1/search`.\
            \ SKIPPED means the record had no indexable text (e.g. only non-searchable\
            \ fields populated) so there was nothing to index — it is stored and retrievable,\
            \ just not full-text/semantic searchable until a searchable field is filled\
            \ in; not an error. FAILED means indexing hit an error — the record is\
            \ still readable but not searchable. Null for a store-only record (`indexMode`\
            \ NONE), which has no indexing to track."
          example: INDEXED
          enum:
          - PENDING_INDEX
          - INDEXED
          - SKIPPED
          - FAILED
        indexMode:
          type: string
          description: "The record's resolved search-index mode: the per-record override\
            \ if set, otherwise the schema's type-level default, otherwise NONE. HYBRID,\
            \ SEMANTIC, and TEXT make the record searchable; NONE is store-only."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        createdBy:
          type: string
          description: "Identifier of the API key or scoped token that created this\
            \ record (for example, `key_abc123` for an API key)."
          example: key_abc123
        createdAt:
          type: string
          description: "When the record was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        updatedAt:
          type: string
          description: "When the record was last updated, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T10:45:00Z
        version:
          type: integer
          description: Optimistic-concurrency version. Pass this value back as `expectedVersion`
            on a later update to avoid silently overwriting a concurrent change —
            the update is rejected with 409 VERSION_CONFLICT if the record changed
            in the meantime. Incremented on every successful write.
          format: int64
          example: 5
      description: "A record: a structured JSON payload that is stored and, optionally,\
        \ indexed for search."
    BatchGetRequest:
      type: object
      properties:
        ids:
          type: array
          description: "The record ids to fetch (maximum 100). Any id you cannot access\
            \ — nonexistent or outside your account or token scope — is silently omitted\
            \ from the response, so a missing id never reveals whether that record\
            \ exists."
          items:
            type: string
            example: 550e8400-e29b-41d4-a716-446655440000
      description: "Request body for fetching multiple records by id in one call.\
        \ Reserved for a future release: the endpoint currently returns HTTP 501 (Not\
        \ Implemented)."
    BatchLookupResponse:
      type: object
      properties:
        results:
          type: array
          description: One result group per lookup you submitted. Match each group
            to its input using the `ref` field.
          items:
            $ref: "#/components/schemas/BatchLookupResult"
      description: "The result of a batch lookup: one result group per lookup you\
        \ submitted. Returned with HTTP 200 even when some lookups did not resolve,\
        \ so inspect each result's status."
    BatchLookupResult:
      type: object
      properties:
        ref:
          type: string
          description: The correlation id echoed from the lookup input that produced
            this result.
          example: row-1
        status:
          type: string
          description: The outcome for this lookup. `resolved` means exactly one match;
            `not_found` means no match; `ambiguous` means the field is not unique
            and matched more than one record.
          example: resolved
          enum:
          - resolved
          - not_found
          - ambiguous
        data:
          type: array
          description: The matched records. Empty unless `status` is `resolved` (one
            record) or `ambiguous` (more than one).
          items:
            $ref: "#/components/schemas/RecordResponse"
      description: The result for a single lookup in a batch-lookup request. Match
        it back to your input using the `ref` field.
    BatchLookupInput:
      type: object
      properties:
        ref:
          type: string
          description: "Your correlation id for this lookup, echoed back on the matching\
            \ result group so you can pair results to inputs regardless of ordering."
          example: row-1
        type:
          type: string
          description: The record type to look up.
          example: intake_form
        field:
          type: string
          description: "The name of the lookup field to match on, as declared on the\
            \ record schema."
          example: email
        value:
          type: string
          description: The exact value to match against the lookup field.
          example: jane@example.com
      description: "A single equality lookup within a batch-lookup request: find the\
        \ records of a given type whose lookup field exactly matches a value."
    BatchLookupRequest:
      type: object
      properties:
        requests:
          type: array
          description: "The lookups to resolve (the `requests` array). Each carries\
            \ a `ref` you supply, echoed back on its result group so you can correlate\
            \ results to inputs."
          items:
            $ref: "#/components/schemas/BatchLookupInput"
      description: "Request body for resolving multiple lookups in one call. Reserved\
        \ for a future release: the endpoint currently returns HTTP 501 (Not Implemented)."
    BatchItemError:
      type: object
      properties:
        code:
          type: string
          description: "A stable, machine-readable code identifying why this item\
            \ failed."
          example: version_conflict
        message:
          type: string
          description: A human-readable explanation of why this item failed.
          example: The record was modified since the expectedVersion you supplied.
      description: Structured error detail for a single failed item in a batch operation.
      nullable: true
    BatchWriteResponse:
      type: object
      properties:
        results:
          type: array
          description: "One result per item you submitted. Match each result to its\
            \ request item by the `index` field, since results are not guaranteed\
            \ to be in request order."
          items:
            $ref: "#/components/schemas/BatchWriteResult"
        succeeded:
          type: integer
          description: The number of items that were created or updated.
          format: int32
          example: 8
        failed:
          type: integer
          description: The number of items that failed (status `conflict` or `invalid`).
          format: int32
          example: 2
      description: "The result of a batch write: a per-item outcome list plus counts\
        \ of how many items succeeded and failed. Returned with HTTP 200 even when\
        \ some items failed, so always inspect the per-item results."
    BatchWriteResult:
      type: object
      properties:
        index:
          type: integer
          description: Zero-based position of this item in the `items` array you submitted.
            Use it to correlate this result with the item you sent.
          format: int32
          example: 2
        id:
          type: string
          description: The id assigned to the written record. Present when this item
            was created or updated; null when it failed (status `conflict` or `invalid`).
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
        externalId:
          type: string
          description: "The `externalId` you supplied for this item, echoed back when\
            \ present. Null when the item carried no `externalId`."
          nullable: true
          example: intake-2024-0042
        status:
          type: string
          description: The outcome for this item. `created` and `updated` are successes;
            `conflict` is a uniqueness or version conflict; `invalid` is a validation
            failure.
          example: created
          enum:
          - created
          - updated
          - conflict
          - invalid
        error:
          $ref: "#/components/schemas/BatchItemError"
      description: "The outcome of a single item in a batch write. Use the `index`\
        \ field to match this result back to the item you submitted, since results\
        \ are not guaranteed to come back in request order."
    BatchWriteRequest:
      type: object
      properties:
        atomicity:
          type: string
          description: "Controls how the batch commits. `all_or_nothing` commits every\
            \ item or none (transactional, but allows a smaller maximum batch size);\
            \ `best_effort` commits each item independently and reports a per-item\
            \ outcome. Defaults to `best_effort`."
          example: best_effort
          enum:
          - all_or_nothing
          - best_effort
        items:
          type: array
          description: The records to write. Each item has the same shape as the body
            of a single create-record request (`POST /v1/records`).
          items:
            $ref: "#/components/schemas/RecordRequest"
      description: "Request body for writing multiple records in one call. Reserved\
        \ for a future release: the endpoint currently returns HTTP 501 (Not Implemented)."
    RecordRequest:
      type: object
      properties:
        typeName:
          type: string
          description: "The record type, matching a schema's record type. Provide\
            \ `typeName` or `schemaId` (at least one is required); when only `schemaId`\
            \ is given, the type is resolved from the schema, and when both are given\
            \ they must agree. Immutable after creation and ignored on update."
          example: intake_form
        schemaId:
          type: string
          description: "Identifier of the schema to validate against. Optional: omit\
            \ it and the server resolves the schema from `typeName` (record types\
            \ are unique within your account and context); supply it (and omit `typeName`)\
            \ to pin a schema and resolve the type from it. Provide `typeName` or\
            \ `schemaId` (at least one); when both are given the server checks that\
            \ they agree. Immutable after creation and ignored on update."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        payload:
          type: object
          additionalProperties: true
          description: "The record's data payload — a JSON object whose structure\
            \ is validated against the referenced schema. Fields the schema marks\
            \ as searchable contribute to full-text and semantic search. On update,\
            \ the supplied object replaces the stored payload in full (it is not key-merged);\
            \ omit it to leave the payload unchanged."
          example:
            first_name: Jane
            email: jane@example.com
        status:
          type: string
          description: Record lifecycle status. Defaults to ACTIVE. Use it to model
            soft-delete or workflow states without physically deleting records.
          example: ACTIVE
        folderId:
          type: string
          description: "Identifier of the folder to group this record with related\
            \ documents. On create, omit to leave the record ungrouped. On update,\
            \ omit to leave it unchanged; once set, this field cannot be cleared."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        userId:
          type: string
          description: "Identifier of the owning user — the Vectros-assigned UUID\
            \ of a user in your account. Optional, and may be set automatically from\
            \ the calling token's identity. Use `GET /v1/users?externalId=` to resolve\
            \ the UUID from your own identifier."
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: "Identifier of the owning organization — the Vectros-assigned\
            \ UUID of an organization in your account. Optional, and may be set automatically\
            \ from the calling token's identity. Use `GET /v1/orgs?externalId=` to\
            \ resolve the UUID from your own identifier."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: "Identifier of the associated client — the Vectros-assigned\
            \ UUID of a client in your account. Optional, and may be set automatically\
            \ from the calling token's identity. Use `GET /v1/clients?externalId=`\
            \ to resolve the UUID from your own identifier."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        externalId:
          type: string
          description: "Your own stable identifier for this record. Optional, and\
            \ immutable after create. Unique within your account, context, and record\
            \ type: posting again with the same `externalId` returns the existing\
            \ record (idempotent create), and it is the key other records reference.\
            \ Maximum 256 characters."
          example: intake-2024-0042
        indexMode:
          type: string
          description: "Per-record override of the search-index mode. HYBRID, SEMANTIC,\
            \ and TEXT index this record for search; NONE is store-only (persisted,\
            \ retrievable by id, and findable by structured-field lookup, but not\
            \ searchable). Optional — omit it to inherit the schema's type-level default\
            \ index mode (which itself defaults to NONE when the schema declares none).\
            \ When both are set, this per-record value wins. Immutable after create."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        expectedVersion:
          type: integer
          description: "Optimistic-concurrency token. Pass the `version` you last\
            \ read (from a fetch or a prior write response) to make this update conditional\
            \ — it is rejected with 409 VERSION_CONFLICT if the record was modified\
            \ since, leaving the stored record untouched. Omit it for last-write-wins\
            \ (the default). Ignored on create."
          format: int64
          example: 5
      description: "Request body for creating or updating a record. Update semantics\
        \ (PUT): omitted fields are preserved — sending null does not clear a field;\
        \ `payload` is replaced in full when supplied (it is not deep-merged); and\
        \ `typeName` and `schemaId` are immutable after creation and ignored on update."
    RecordLookupResponse:
      type: object
      properties:
        data:
          type: array
          description: "The records matched by this lookup, in lookup order (ascending\
            \ by field value for range and prefix lookups; lookup-key order for exact-match\
            \ lookups)."
          items:
            $ref: "#/components/schemas/RecordResponse"
        nextCursor:
          type: string
          description: Opaque cursor for the next page. Pass it back as the `startFrom`
            parameter to continue. Null when there are no more results. Treat it as
            opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: A page of matching records plus an opaque cursor for the next page.
    RecordLookupRequest:
      required:
      - field
      - type
      type: object
      properties:
        type:
          type: string
          description: "The record type to look up (for example, `intake_form`)."
          example: intake_form
        field:
          type: string
          description: "Name of the lookup field declared on the schema. For a field\
            \ marked sensitive, this body variant is required (the GET variant rejects\
            \ looking up by a sensitive value)."
          example: ssn
        value:
          type: string
          description: "Exact value to match. Mutually exclusive with `from`/`to`\
            \ and `prefix`. Sensitive fields can only be looked up by exact value,\
            \ and only through this body variant."
          example: 123-45-6789
        from:
          type: string
          description: Inclusive lower bound for a range lookup (requires `to`; non-sensitive
            fields only). Mutually exclusive with `value` and `prefix`.
          example: 2024-01-01
        to:
          type: string
          description: Inclusive upper bound for a range lookup (requires `from`).
          example: 2024-12-31
        prefix:
          type: string
          description: "Prefix to match (string, non-sensitive fields only). Mutually\
            \ exclusive with `value` and `from`/`to`."
          example: jane
        startFrom:
          type: string
          description: Pagination cursor — pass the `nextCursor` returned by the previous
            page.
          example: 550e8400-e29b-41d4-a716-446655440000
        limit:
          maximum: 100
          minimum: 1
          type: integer
          description: Maximum number of records to return per page (1–100; defaults
            to 20).
          format: int32
          example: 20
          default: 20
        order:
          type: string
          description: "Sort direction for the returned results: `asc` (default) or\
            \ `desc`."
          example: asc
          default: asc
          enum:
          - asc
          - desc
        includePayload:
          type: boolean
          description: "When true, externalized record payloads are returned in full\
            \ in this response (capped per page). Defaults to false, which returns\
            \ only the indexed projection."
          example: false
      description: "Request body for `POST /v1/records/lookup`. Carries the lookup\
        \ parameters in the body so a sensitive field's value never travels in the\
        \ URL query string. Provide exactly one lookup mode: `value` (exact match),\
        \ `from` plus `to` (range), or `prefix`."
    RecordPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/RecordResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of records in a `{data, nextCursor}` envelope: `data` is\
        \ the array of records on this page, and `nextCursor` is an opaque pagination\
        \ cursor. Pass it as `startFrom` to fetch the next page; it is null when no\
        \ more pages remain."
    FieldDef:
      required:
      - fieldId
      - fieldType
      type: object
      properties:
        fieldId:
          type: string
          description: Unique identifier for this field within the schema.
          example: first_name
        fieldType:
          type: string
          description: "Data type of the field. Use `reference` for a foreign-key\
            \ field that points at another type — then set `targetTypeName`, `targetField`,\
            \ `targetSurface`, and `cardinality` below."
          example: string
          enum:
          - string
          - number
          - boolean
          - date
          - enum
          - array
          - object
          - reference
        required:
          type: boolean
          description: Whether this field is required when a record is created.
          example: true
        searchable:
          type: boolean
          description: Whether this field's value contributes to full-text search.
          example: true
        filterable:
          type: boolean
          description: Whether this field can be used as a filter in queries.
          example: false
        description:
          type: string
          description: Human-readable description of the field.
          example: Client's first name
        validation:
          type: object
          description: Validation rules for this field's value (such as minimum/maximum
            length or a regex pattern).
        enumValues:
          type: array
          description: "The set of allowed values, for an enum-type field."
          items:
            type: object
            description: "The set of allowed values, for an enum-type field."
        targetTypeName:
          type: string
          description: "For a `reference` field: the target type this field points\
            \ at. Required for reference fields."
          example: patient
        targetField:
          type: string
          description: "For a `reference` field: the target's lookup field to resolve\
            \ against. Defaults to `externalId`, and must name a unique lookup on\
            \ the target type."
          example: externalId
        cardinality:
          type: string
          description: "For a `reference` field: `one` (a single id, the default)\
            \ or `many` (an array of ids)."
          example: one
        targetSurface:
          type: string
          description: "For a `reference` field: which surface the target lives on\
            \ — record, document, user, org, or client. Required for reference fields.\
            \ The same type name can exist on more than one surface, so this disambiguates\
            \ which lookup resolves the reference."
          example: record
          enum:
          - record
          - document
          - user
          - org
          - client
        sensitive:
          type: boolean
          description: "Marks the field as sensitive (PHI/PII). Sensitive fields are\
            \ redacted in logs, audit trails, and errors; blind-indexed for lookups;\
            \ excluded from search; and masked in responses unless the token carries\
            \ the `s` reveal scope for this record type."
          example: false
      description: "Defines a single field's type, validation, and indexing behavior."
    LookupDef:
      required:
      - fieldName
      type: object
      properties:
        fieldName:
          type: string
          description: Name of the payload field to index.
          example: email
        unique:
          type: boolean
          description: "If true, enforces that this field's value is unique within\
            \ your account."
          example: true
        rangeEnabled:
          type: boolean
          description: "If true, this field also supports ordered range and prefix\
            \ lookups (`from`/`to`/`prefix`) in addition to exact-match lookups. Range-enabled\
            \ fields are billed at the range-index rate. Cannot be set on a sensitive\
            \ field, because a blind index is not orderable. This setting is locked\
            \ once the field is created."
          example: false
        sortBy:
          type: string
          description: "For an exact-match lookup, the index sort key: `createdAt`\
            \ (the default), `lastUpdated`, or a declared field on this schema. This\
            \ setting is locked once the field is created."
          example: createdAt
        allowOverflow:
          type: boolean
          description: "Each schema has a fixed budget of fast exact-match lookup\
            \ indexes. An exact-match lookup beyond that budget is rejected unless\
            \ you set this flag, which opts the field into a higher-cost secondary\
            \ index (more storage and transactional writes). Range-enabled lookups\
            \ do not count against the budget. This flag has no effect on a field\
            \ that fits within the budget."
          example: false
      description: Declares a payload field that is indexed for direct lookup queries.
      example:
      - fieldName: email
        unique: true
    RenderHintDef:
      type: object
      properties:
        label:
          type: string
          description: Display label for the field.
          example: First Name
        widget:
          type: string
          description: "Widget used to render the field. One of: text, textarea, select,\
            \ date, checkbox."
          example: text
          enum:
          - text
          - textarea
          - select
          - date
          - checkbox
        order:
          type: integer
          description: Display order; lower values appear first.
          format: int32
          example: 1
        section:
          type: string
          description: Section heading used to group related fields.
          example: Personal Info
        helpText:
          type: string
          description: Help text shown to the user beneath the field.
          example: Enter the client's legal first name
        displayField:
          type: boolean
          description: Marks this field as the record's headline (display) field —
            the linked primary column in the records list and the title on the detail
            view. At most one per schema.
          example: true
      description: UI rendering hint for a single field.
    SchemaResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for this schema.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        typeName:
          type: string
          description: Type identifier for this schema.
          example: intake_form
        displayName:
          type: string
          description: Human-readable display name.
          example: Client Intake Form
        description:
          type: string
          description: Description of what this schema is for.
          example: Captures initial client information during onboarding
        fields:
          type: array
          description: Field definitions used to validate record payloads.
          items:
            $ref: "#/components/schemas/FieldDef"
        lookupFields:
          type: array
          description: Payload fields indexed for direct lookup queries.
          items:
            $ref: "#/components/schemas/LookupDef"
        renderHints:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/RenderHintDef"
          description: UI rendering hints keyed by field id.
        capabilities:
          type: object
          additionalProperties:
            type: boolean
            description: Feature flags for this schema. `auditHistory` (default true)
              controls whether a change-history (version) trail is kept for this type's
              data; it never affects the records themselves. `readAccessLog` (default
              false) controls accounting-of-disclosures logging for reads of this
              type's records.
            example: false
          description: Feature flags for this schema. `auditHistory` (default true)
            controls whether a change-history (version) trail is kept for this type's
            data; it never affects the records themselves. `readAccessLog` (default
            false) controls accounting-of-disclosures logging for reads of this type's
            records.
          example:
            auditHistory: true
            readAccessLog: false
        storageProfile:
          type: string
          description: "Payload storage profile: STANDARD (externalize large payloads\
            \ to S3), LOW_LATENCY (always store inline), or LARGE_PAYLOAD (always\
            \ externalize). Defaults to STANDARD."
          example: STANDARD
          enum:
          - STANDARD
          - LOW_LATENCY
          - LARGE_PAYLOAD
        indexMode:
          type: string
          description: "Default search-index mode for this type's records: HYBRID,\
            \ SEMANTIC, TEXT, or NONE (store-only, not searchable)."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        allowedSurfaces:
          type: array
          description: "Which typed surfaces may bind this schema (record, document,\
            \ user, org, or client). Always present (it is required at write time)."
          example:
          - record
          items:
            type: string
            description: "Which typed surfaces may bind this schema (record, document,\
              \ user, org, or client). Always present (it is required at write time)."
            example: "[\"record\"]"
            enum:
            - record
            - document
            - user
            - org
            - client
          enum:
          - record
          - document
          - user
          - org
          - client
        active:
          type: boolean
          description: Whether this schema is active.
          example: true
        schemaVersion:
          type: integer
          description: "Schema version number, incremented each time the schema definition\
            \ changes."
          format: int32
          example: 1
        createdAt:
          type: string
          description: "When the schema was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        updatedAt:
          type: string
          description: "When the schema was last modified, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T10:45:00Z
      description: "A record schema, defining the structure and behavior of a record\
        \ type."
    SchemaRequest:
      required:
      - allowedSurfaces
      - displayName
      - typeName
      type: object
      properties:
        typeName:
          type: string
          description: Unique type identifier for this schema. Immutable after creation.
          example: intake_form
        displayName:
          type: string
          description: "Human-readable display name for this type, shown in UIs."
          example: Client Intake Form
        description:
          type: string
          description: Optional description of what this schema is for.
          example: Captures initial client information during onboarding
        fields:
          type: array
          description: Field definitions used to validate record payloads. Omit for
            a bare schema that performs no payload validation.
          items:
            $ref: "#/components/schemas/FieldDef"
        lookupFields:
          type: array
          description: Payload fields to index for direct lookup queries. Maximum
            10 lookup fields.
          example:
          - fieldName: email
            unique: true
          items:
            $ref: "#/components/schemas/LookupDef"
        renderHints:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/RenderHintDef"
          description: UI rendering hints keyed by field id.
        capabilities:
          type: object
          additionalProperties:
            type: boolean
            description: "Feature flags for this schema. `auditHistory` (default true)\
              \ controls whether a change-history (version) trail is kept for this\
              \ type's data; setting it false stops recording the trail but never\
              \ affects your record or document data itself. `readAccessLog` (default\
              \ false; opt-in) controls accounting-of-disclosures logging: when true,\
              \ each governed read of this schema's records writes a retained, subject-enumerable\
              \ access record. The effective value resolves in order: this schema\
              \ flag, then your context's default, then the platform default (false)."
            example: false
          description: "Feature flags for this schema. `auditHistory` (default true)\
            \ controls whether a change-history (version) trail is kept for this type's\
            \ data; setting it false stops recording the trail but never affects your\
            \ record or document data itself. `readAccessLog` (default false; opt-in)\
            \ controls accounting-of-disclosures logging: when true, each governed\
            \ read of this schema's records writes a retained, subject-enumerable\
            \ access record. The effective value resolves in order: this schema flag,\
            \ then your context's default, then the platform default (false)."
          example:
            auditHistory: true
            readAccessLog: false
        indexMode:
          type: string
          description: "Default search-index mode for instances of this type — applies\
            \ to records of this type and to documents bound to this schema, unless\
            \ the individual record or document sets its own indexMode. HYBRID, SEMANTIC,\
            \ and TEXT make content searchable; NONE is store-only (the data is persisted,\
            \ retrievable, and usable for lookups, but not searchable). Omit for no\
            \ default: a record with no indexMode of its own then defaults to NONE,\
            \ and a document with no indexMode is rejected."
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
          - NONE
        storageProfile:
          type: string
          description: "Where this type's payload is stored. STANDARD (default): payloads\
            \ of 4 KB or larger are externalized to S3, smaller ones are stored inline.\
            \ LOW_LATENCY: always store inline for the lowest read latency. LARGE_PAYLOAD:\
            \ always externalize to S3, which lifts the 400 KB item-size ceiling.\
            \ For externalized types, list and lookup responses return only the indexed\
            \ projection unless you pass `includePayload=true`."
          example: STANDARD
          enum:
          - STANDARD
          - LOW_LATENCY
          - LARGE_PAYLOAD
        allowedSurfaces:
          type: array
          description: "Which typed surfaces may bind this schema by its id: record,\
            \ document, user, org, or client. Required and must be non-empty. A schema\
            \ may list several surfaces (for a shared type usable on both records\
            \ and documents). This drives surface-scoped schema listing (`GET /v1/schemas?surface=`)\
            \ and is enforced at bind time — for example, a document cannot bind a\
            \ record-only schema."
          example:
          - record
          items:
            type: string
            description: "Which typed surfaces may bind this schema by its id: record,\
              \ document, user, org, or client. Required and must be non-empty. A\
              \ schema may list several surfaces (for a shared type usable on both\
              \ records and documents). This drives surface-scoped schema listing\
              \ (`GET /v1/schemas?surface=`) and is enforced at bind time — for example,\
              \ a document cannot bind a record-only schema."
            example: "[\"record\"]"
            enum:
            - record
            - document
            - user
            - org
            - client
          enum:
          - record
          - document
          - user
          - org
          - client
        active:
          type: boolean
          description: Whether this schema is active. An inactive schema rejects creation
            of new records of its type.
          example: true
        userId:
          type: string
          description: "Owning user — the Vectros-assigned UUID of a user in your\
            \ account. Optional; omit to create an account-wide shared schema. With\
            \ an API key, this sets the schema's owner explicitly. With a scoped token,\
            \ the user must match the token's identity claim (if set) or fall within\
            \ the token's data scope."
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: "Owning organization — the Vectros-assigned UUID of an organization\
            \ in your account. Optional. With an API key, this sets the schema's owning\
            \ organization explicitly. With a scoped token, it must match the token's\
            \ identity claim (if set) or fall within the token's data scope."
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: "Associated client — the Vectros-assigned UUID of a client\
            \ in your account. Optional. With an API key, this sets the schema's client\
            \ explicitly. With a scoped token, it must match the token's identity\
            \ claim (if set) or fall within the token's data scope."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
      description: "Request body for creating or updating a record schema. On update\
        \ (PUT), fields you omit are preserved and `typeName` cannot be changed; collection\
        \ fields (`fields`, `lookupFields`, `renderHints`, `capabilities`) are replaced\
        \ wholesale when supplied, never merged element-by-element."
    SchemaPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/SchemaResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of record schemas as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    SearchResponse:
      type: object
      properties:
        results:
          type: array
          description: "The matched content chunks, ordered from most to least relevant."
          items:
            $ref: "#/components/schemas/SearchResult"
        totalResults:
          type: integer
          description: "The total number of items that matched the query (the size\
            \ of the matching pool). This may be larger than the number of results\
            \ returned in this response, which is capped by `limit`."
          format: int32
          example: 42
        searchTimeMs:
          type: integer
          description: "How long the search took to run, in milliseconds."
          format: int64
          example: 85
        degraded:
          type: boolean
          description: "True when one of the two search engines was unavailable and\
            \ these results came from the surviving engine only (for example, keyword\
            \ search fell back to semantic-only during an outage). When true, the\
            \ results may be less complete than a healthy hybrid search. Set `requireComplete`\
            \ to true on your request to receive a 503 instead of partial results."
          example: false
        degradedLegs:
          type: array
          description: "Which search engines were unavailable for this response: `text`\
            \ (keyword) and/or `vector` (semantic). Empty when the search was not\
            \ degraded."
          items:
            type: string
            description: "Which search engines were unavailable for this response:\
              \ `text` (keyword) and/or `vector` (semantic). Empty when the search\
              \ was not degraded."
      description: "The result of a search: the matched content chunks plus metadata\
        \ about the search run."
    SearchResult:
      type: object
      properties:
        documentId:
          type: string
          description: "The ID of the source document or record this chunk belongs\
            \ to. Use it with `GET /v1/documents/{id}` or `GET /v1/records/{id}` (depending\
            \ on `sourceType`) to retrieve the full item."
          example: 550e8400-e29b-41d4-a716-446655440000
        score:
          type: number
          description: The combined relevance score that fuses the text and semantic
            signals (reciprocal rank fusion). This is the primary sort key — a higher
            value means a more relevant result.
          format: double
          example: 0.85
        textScore:
          type: number
          description: "The keyword (BM25) relevance component of the score. It is\
            \ non-zero in HYBRID mode when the keyword engine contributes to the match.\
            \ It is always 0 in TEXT mode, because that path does not expose per-hit\
            \ scores — in TEXT mode the order of the results array is the ranking\
            \ signal, not this field."
          format: double
          example: 0.72
        semanticScore:
          type: number
          description: The semantic (vector similarity) component of the score. Non-zero
            when the search mode is SEMANTIC or HYBRID.
          format: double
          example: 0.91
        chunkText:
          type: string
          description: The exact text chunk that matched the query. Pass this to your
            model as the retrieved context.
        contextText:
          type: string
          description: A broader passage surrounding the matched chunk. Use this instead
            of `chunkText` when you need more context — for example to avoid fragmentation
            when building a long-context prompt for a model.
        snippet:
          type: string
          description: A short excerpt with the matched query terms highlighted. Suitable
            for display in a search results UI.
        sourceType:
          type: string
          description: "Indicates what kind of source item this result came from:\
            \ `PartnerDocument` for documents ingested via `POST /v1/documents` or\
            \ `POST /v1/documents/upload`, and `GenericRecord` for typed records created\
            \ via `POST /v1/records`. Use this to branch when rendering a mix of documents\
            \ and records from a unified search."
          example: PartnerDocument
          enum:
          - PartnerDocument
          - GenericRecord
        metadata:
          type: object
          additionalProperties: true
          description: "The metadata you supplied for this item when you ingested\
            \ it, such as its title, folder ID, and any custom fields you included."
        createdAt:
          type: string
          description: "When the source item was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
      description: A single content chunk that matched the search query.
    SearchRequest:
      required:
      - query
      type: object
      properties:
        query:
          type: string
          description: "The search query, expressed in natural language or as keywords.\
            \ Required."
          example: patient intake form diabetes
        mode:
          type: string
          description: How results are ranked. HYBRID combines semantic and keyword
            ranking (recommended). SEMANTIC ranks by vector similarity only. TEXT
            ranks by keyword matching only. Defaults to HYBRID.
          example: HYBRID
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
        limit:
          maximum: 100
          minimum: 1
          type: integer
          description: The maximum number of results to return. Must be between 1
            and 100. Defaults to 20.
          format: int32
          example: 20
        offset:
          minimum: 0
          type: integer
          description: "The number of results to skip, for pagination. Must be between\
            \ 0 and 200. Defaults to 0."
          format: int32
          example: 0
        userId:
          type: string
          description: Restrict results to content owned by this user — the Vectros-assigned
            UUID of a user in your account. Use `GET /v1/users?externalId=` to look
            up a user's ID from your own identifier.
          example: 550e8400-e29b-41d4-a716-446655440000
        orgId:
          type: string
          description: Restrict results to content belonging to this organization
            — the Vectros-assigned UUID of an organization in your account. Use `GET
            /v1/orgs?externalId=` to look up an organization's ID from your own identifier.
          example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        clientId:
          type: string
          description: Restrict results to content associated with this client — the
            Vectros-assigned UUID of a client in your account. Use `GET /v1/clients?externalId=`
            to look up a client's ID from your own identifier.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        filters:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/FilterValue'
          description: "Field-level filters applied to your document and record metadata.\
            \ Each key is a field name, and top-level keys are AND-combined. Each\
            \ value is one of: a scalar (string, number, or boolean) for an exact\
            \ match; an array of scalars to match any one of them; or an operator\
            \ map for ranges, negation, and membership. The supported operators are\
            \ a closed set: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte` (each takes\
            \ a scalar) and `$in`, `$nin` (each takes an array of scalars). Operators\
            \ within one map are AND-combined, so `{\"price\":{\"$gte\":100,\"$lte\"\
            :500}}` expresses a closed range; `$in` and `$nin` may not be combined\
            \ with other operators. Numbers and booleans are matched by type, so the\
            \ field must have been ingested under a typed schema; dates may be sent\
            \ as ISO 8601 strings or epoch milliseconds. Unknown operators, non-scalar\
            \ operands, and malformed field names are rejected with a 400."
          example:
            status: ACTIVE
            tag:
            - red
            - blue
            price:
              $gte: 100
              $lte: 500
            state:
              $ne: archived
        textMode:
          type: string
          description: "How query terms are matched by the keyword engine when the\
            \ mode is TEXT or HYBRID. OR matches content containing any query term\
            \ (broadest recall). AND requires every term to be present (higher precision).\
            \ PHRASE requires the terms to appear as a contiguous sequence. COMPLEX\
            \ enables the full query syntax, including boolean operators, field-scoped\
            \ queries, and range filters. When omitted, defaults to PHRASE (with a\
            \ slop of 3) in HYBRID mode and OR in TEXT mode."
          example: OR
          enum:
          - OR
          - AND
          - PHRASE
          - COMPLEX
        minSimilarity:
          type: number
          description: "The minimum semantic similarity score a result must have to\
            \ be included, on a scale of 0.0 to 1.0. Results scoring below this threshold\
            \ are dropped. Applies when the mode is SEMANTIC or HYBRID."
          format: double
          example: 0.7
        minTextRelevance:
          maximum: 1
          minimum: 0
          type: number
          description: "A relative relevance floor for the keyword leg, given as a\
            \ fraction from 0 to 1 of the TOP result's score — results scoring below\
            \ (top score × this value) are dropped. Keyword scores are unbounded and\
            \ depend on the query, so this is a relative cutoff rather than an absolute\
            \ score: for example, 0.5 keeps only results at least half as relevant\
            \ as the best hit. Applies when the mode is TEXT or HYBRID. Omit it (or\
            \ use a value of 0 or less) to keep all keyword matches."
          format: double
          example: 0.5
        slop:
          minimum: 0
          type: integer
          description: "The phrase-match slop: the number of intervening positions\
            \ allowed between query terms when `textMode` is PHRASE (0 means the terms\
            \ must be exactly adjacent). Ignored for the other text modes."
          format: int32
          example: 2
        uniqueDocuments:
          type: boolean
          description: "When true, returns at most one chunk per source item, so a\
            \ single document or record cannot appear more than once in the results."
          example: true
        contentTypes:
          type: array
          description: "Narrow results to specific content types. When omitted, empty,\
            \ or set to both values, the search returns all content (documents and\
            \ records) in a single unified result set, which is the default behavior.\
            \ Each returned result carries a `sourceType` discriminator so you can\
            \ tell documents and records apart. Pass `[\"documents\"]` for documents\
            \ only, or `[\"records\"]` for records only."
          example:
          - documents
          - records
          items:
            type: string
            description: "Narrow results to specific content types. When omitted,\
              \ empty, or set to both values, the search returns all content (documents\
              \ and records) in a single unified result set, which is the default\
              \ behavior. Each returned result carries a `sourceType` discriminator\
              \ so you can tell documents and records apart. Pass `[\"documents\"\
              ]` for documents only, or `[\"records\"]` for records only."
            example: "[\"documents\",\"records\"]"
            enum:
            - documents
            - records
          enum:
          - documents
          - records
        folderId:
          type: string
          description: "Restrict results to content (documents or records) in this\
            \ EXACT folder. Folders hold a mix of documents and records. Provide the\
            \ Vectros-assigned UUID of a folder; use `GET /v1/folders` to list your\
            \ folders. To match a folder AND all of its descendants, use `rootFolderId`\
            \ instead."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        rootFolderId:
          type: string
          description: Restrict results to content (documents or records) anywhere
            under this folder subtree — the folder itself and all of its descendants.
            Provide the Vectros-assigned UUID of a top-level (root) folder. Applies
            to documents and records alike. Use `folderId` instead to match a single
            exact folder.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        typeName:
          type: string
          description: "Restrict results to records of a specific schema type (for\
            \ example `patient` or `intake_form`). Has no effect on documents — it\
            \ only narrows record results. Setting it implicitly limits the results\
            \ to records, unless you also include documents via `contentTypes`."
          example: patient
        createdAfter:
          type: string
          description: "Restrict results to content created at or after this ISO-8601\
            \ UTC timestamp. Useful for incremental queries (for example, finding\
            \ anything ingested in the last hour), for isolating just-created content\
            \ from older history, and for time-bounded analytics. Pair it with `createdBefore`\
            \ to define a window."
          example: 2026-05-27T14:00:00Z
        createdBefore:
          type: string
          description: "Restrict results to content created at or before this ISO-8601\
            \ UTC timestamp. Pair it with `createdAfter` to define a window, or use\
            \ it alone to find content older than a cutoff (for example, in archival\
            \ or cleanup workflows)."
          example: 2026-05-27T15:00:00Z
        requireComplete:
          type: boolean
          description: "A fail-closed override. When true, the request returns HTTP\
            \ 503 instead of partial results if one of the search engines is unavailable.\
            \ Defaults to false, in which case an outage degrades to the surviving\
            \ engine and the response carries `degraded: true` along with the failed\
            \ engines in `degradedLegs`. Set this to true only when complete results\
            \ are required and a degraded answer is unacceptable."
          example: false
      description: The request body for a hybrid search over your documents and records.
    MintTokenResponse:
      required:
      - expiresAt
      - token
      type: object
      properties:
        token:
          type: string
          description: "The opaque bearer token. Send it as `Authorization: Bearer\
            \ <token>` on subsequent requests."
          example: st_eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...
        expiresAt:
          type: integer
          description: "When the token expires, as seconds since the Unix epoch (matches\
            \ the token's `exp` claim)."
          format: int64
          example: 1705312200
      description: "A minted scoped token and its absolute expiration. The token is\
        \ an opaque string for use in `Authorization: Bearer <token>` headers; `expiresAt`\
        \ mirrors the token's `exp` claim (seconds since the Unix epoch) so you can\
        \ decide when to refresh without decoding the token."
    ScopeRequest:
      type: object
      properties:
        allowedActions:
          type: array
          description: "The actions this token may perform. Each entry has the form\
            \ `resource:operations`, where `operations` is one of `r` (read), `c`\
            \ (create), `u` (update), `d` (delete), `crud` (all four), or a colon-separated\
            \ combination. For records you may append a type qualifier, e.g. `records:r:intake_form`.\
            \ Valid resources are: `clients`, `orgs`, `users`, `documents`, `folders`,\
            \ `records`, `schemas`, and `search`."
          example:
          - records:crud
          - schemas:r
          - search:r
          items:
            type: string
            description: "The actions this token may perform. Each entry has the form\
              \ `resource:operations`, where `operations` is one of `r` (read), `c`\
              \ (create), `u` (update), `d` (delete), `crud` (all four), or a colon-separated\
              \ combination. For records you may append a type qualifier, e.g. `records:r:intake_form`.\
              \ Valid resources are: `clients`, `orgs`, `users`, `documents`, `folders`,\
              \ `records`, `schemas`, and `search`."
            example: "[\"records:crud\",\"schemas:r\",\"search:r\"]"
        identity:
          type: object
          additionalProperties:
            type: string
            description: "Ownership fields automatically stamped onto resources created\
              \ with this token. Optional. Keys are ownership fields (`userId`, `orgId`,\
              \ `clientId`); values must be Vectros entity UUIDs — look them up with\
              \ `GET /v1/users`, `/v1/orgs`, or `/v1/clients`. The token holder cannot\
              \ override these values when creating resources."
            example: "{\"userId\":\"550e8400-e29b-41d4-a716-446655440000\"}"
          description: "Ownership fields automatically stamped onto resources created\
            \ with this token. Optional. Keys are ownership fields (`userId`, `orgId`,\
            \ `clientId`); values must be Vectros entity UUIDs — look them up with\
            \ `GET /v1/users`, `/v1/orgs`, or `/v1/clients`. The token holder cannot\
            \ override these values when creating resources."
          example:
            userId: 550e8400-e29b-41d4-a716-446655440000
        dataScope:
          type: object
          additionalProperties:
            type: array
            description: "Restricts which records the token can access. Optional.\
              \ Keys are ownership fields (`userId`, `orgId`, `clientId`); values\
              \ are arrays of permitted entity UUIDs — the token can only access records\
              \ whose ownership field matches one of these values. Every non-null\
              \ UUID must be a real entity in your account. Include a JSON `null`\
              \ in the array (e.g. `[\"uuid\", null]`) to ALSO grant access to records\
              \ whose ownership field is unset — this is the explicit \"records with\
              \ no assigned org, client, or user\" sentinel, NOT a wildcard. To grant\
              \ access regardless of value, omit the key from the data scope entirely."
            example:
              orgId:
              - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
            items:
              type: string
              description: "Restricts which records the token can access. Optional.\
                \ Keys are ownership fields (`userId`, `orgId`, `clientId`); values\
                \ are arrays of permitted entity UUIDs — the token can only access\
                \ records whose ownership field matches one of these values. Every\
                \ non-null UUID must be a real entity in your account. Include a JSON\
                \ `null` in the array (e.g. `[\"uuid\", null]`) to ALSO grant access\
                \ to records whose ownership field is unset — this is the explicit\
                \ \"records with no assigned org, client, or user\" sentinel, NOT\
                \ a wildcard. To grant access regardless of value, omit the key from\
                \ the data scope entirely."
              example: "{\"orgId\":[\"6ba7b810-9dad-11d1-80b4-00c04fd430c8\"]}"
          description: "Restricts which records the token can access. Optional. Keys\
            \ are ownership fields (`userId`, `orgId`, `clientId`); values are arrays\
            \ of permitted entity UUIDs — the token can only access records whose\
            \ ownership field matches one of these values. Every non-null UUID must\
            \ be a real entity in your account. Include a JSON `null` in the array\
            \ (e.g. `[\"uuid\", null]`) to ALSO grant access to records whose ownership\
            \ field is unset — this is the explicit \"records with no assigned org,\
            \ client, or user\" sentinel, NOT a wildcard. To grant access regardless\
            \ of value, omit the key from the data scope entirely."
          example:
            orgId:
            - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
      description: The actions and data a scoped token is allowed to access.
    TokenRequest:
      required:
      - scope
      type: object
      properties:
        userId:
          type: string
          description: "The Vectros user ID of the end user this token is minted for.\
            \ This is the UUID returned when you created the user via `POST /v1/users`.\
            \ Optional — omit it for service-to-service tokens that have no specific\
            \ user context. If provided, it must reference a real user in your account.\
            \ Use `GET /v1/users?externalId={yourId}` to resolve your own system's\
            \ user ID to the Vectros user ID."
          example: 550e8400-e29b-41d4-a716-446655440000
        scope:
          $ref: "#/components/schemas/ScopeRequest"
        expiresInSeconds:
          type: integer
          description: "How long the token remains valid, in seconds. Maximum 86400\
            \ (24 hours); defaults to 3600 (1 hour)."
          format: int32
          example: 3600
      description: Request body for minting a scoped token.
    UserResponse:
      type: object
      properties:
        id:
          type: string
          description: "The Vectros-assigned UUID for this user. Use this ID when\
            \ referencing the user in token minting, record ownership, and filters."
          example: 550e8400-e29b-41d4-a716-446655440000
        externalId:
          type: string
          description: "Your own identifier for this user, supplied when the user\
            \ was created."
          example: usr_12345
        email:
          type: string
          description: The user's email address.
          example: user@example.com
        status:
          type: string
          description: The user's lifecycle status. `ACTIVE` and `SUSPENDED` apply
            to normal users; `PENDING` marks an invited user who has not yet completed
            acceptance.
          example: ACTIVE
          enum:
          - ACTIVE
          - SUSPENDED
          - PENDING
        type:
          type: string
          description: "The kind of user: `HUMAN` for a real person, or `SERVICE`\
            \ for a bot, agent, or automated integration. Users created before this\
            \ field existed are reported as `HUMAN`."
          example: HUMAN
          enum:
          - HUMAN
          - SERVICE
        payload:
          type: object
          additionalProperties: true
          description: Arbitrary key-value attributes stored on this user.
        schemaId:
          type: string
          description: "The ID of the record schema that governs this user's payload\
            \ validation and lookup indexing, or null if the user is not bound to\
            \ a schema."
          example: 550e8400-e29b-41d4-a716-446655440000
        schemaVersion:
          type: integer
          description: The version of the governing schema captured at the time this
            user was last written. The user keeps the version it was stamped with
            even after the schema is edited. Null when the user is not bound to a
            schema.
          format: int32
          example: 3
        createdAt:
          type: string
          description: "When the user was created, as an ISO-8601 UTC timestamp."
          example: 2024-01-15T09:30:00Z
        externalSubject:
          type: string
          description: "The user's identifier in your own authentication system (for\
            \ example, a Cognito sub or Auth0 user ID). This is set when an invitation\
            \ is accepted via PUT /v1/users/{id} with status=ACTIVE, and is null until\
            \ the user is activated."
          example: auth0|abc123
      description: A user identity that you control within your account.
    UserRequest:
      required:
      - externalId
      type: object
      properties:
        externalId:
          type: string
          description: "Your own unique identifier for this user. Drives idempotent\
            \ upsert: if a user with this `externalId` already exists, it is returned\
            \ instead of creating a duplicate."
          example: usr_12345
        email:
          type: string
          description: The user's email address. Used for display and notifications
            only; it is not used for authentication to the Vectros API.
          example: user@example.com
        status:
          type: string
          description: "The user's lifecycle status. `ACTIVE` users can be used normally;\
            \ `SUSPENDED` users are retained but blocked from new operations. `PENDING`\
            \ is a server-managed state for invitations awaiting acceptance — you\
            \ cannot set it directly. (A pending user becomes `ACTIVE` by sending\
            \ status=ACTIVE together with `inviteToken`, `externalSubject`, and `emailVerifiedAttestation=true`.)\
            \ You may set only `ACTIVE` or `SUSPENDED` directly."
          example: ACTIVE
          enum:
          - ACTIVE
          - SUSPENDED
          - PENDING
        type:
          type: string
          description: "The kind of user. `HUMAN` (the default) is a real person;\
            \ `SERVICE` is a bot, agent, scheduled job, or other named integration\
            \ with no specific person behind it. Service users may omit `email`. The\
            \ type is immutable after creation — to change it, create a new user."
          example: HUMAN
          enum:
          - HUMAN
          - SERVICE
        payload:
          type: object
          additionalProperties: true
          description: "Arbitrary key-value attributes to store on the user, useful\
            \ for carrying additional data from your own system."
          example:
            role: clinician
            department: cardiology
        schemaId:
          type: string
          description: "Optional ID of a record schema (created via POST /v1/schemas)\
            \ that drives payload validation and lookup indexing. When set, the schema\
            \ must belong to your account, the payload is validated against the schema's\
            \ fields on save, and the schema's lookup fields become queryable via\
            \ GET /v1/users (using `type`, `field`, and a lookup mode). When omitted,\
            \ the payload is free-form and no lookup rows are written."
          example: 550e8400-e29b-41d4-a716-446655440000
        inviteToken:
          type: string
          description: The raw invite token from the invitation email link. Required
            to activate a pending invitation (moving the user from PENDING to ACTIVE);
            ignored otherwise. The token's signature is verified server-side.
          example: inv_eyJhbGc...
        externalSubject:
          type: string
          description: "The user's identifier in your own authentication system (for\
            \ example, a Cognito sub or Auth0 user ID). Required to activate a pending\
            \ invitation; ignored on subsequent updates. Treat it as immutable once\
            \ the user is activated."
          example: auth0|abc123
        emailVerifiedAttestation:
          type: boolean
          description: "Your attestation that the invitee's email was verified by\
            \ your authentication system before this call. Must be `true` to activate\
            \ a pending invitation. Vectros cannot inspect your authentication system,\
            \ so asserting this accurately is your responsibility."
          example: true
      description: Request body for creating or updating a user.
    UserPage:
      type: object
      properties:
        data:
          type: array
          description: "The items on this page, in the endpoint's natural order. Empty\
            \ when there are no results."
          items:
            $ref: "#/components/schemas/UserResponse"
        nextCursor:
          type: string
          description: An opaque cursor for the next page. Pass it back as the `startFrom`
            query parameter to continue paging; null when there are no more results.
            Treat it as opaque — do not parse or construct it.
          nullable: true
          example: 550e8400-e29b-41d4-a716-446655440000
      description: "A page of users as a `{data, nextCursor}` envelope. `nextCursor`\
        \ is an opaque cursor — pass it back as `startFrom` to fetch the next page,\
        \ or null when no more pages remain."
    CreateInviteResponse:
      type: object
      properties:
        userId:
          type: string
          description: "Server-generated identifier of the invited user. Stable for\
            \ the life of the user, including after they accept and become active."
          example: 550e8400-e29b-41d4-a716-446655440000
        inviteExpiresAt:
          type: string
          description: "When the invitation token expires, as an ISO-8601 UTC timestamp.\
            \ Accept requests made after this time are rejected."
          example: 2026-07-01T15:00:00Z
        emailSent:
          type: boolean
          description: Whether the invitation email was sent. True only when you requested
            delivery (`sendEmail` true) and the send succeeded.
          example: true
        inviteToken:
          type: string
          description: "The raw invitation token. Returned only when you set `sendEmail`\
            \ to false. Treat it as a sensitive credential: anyone holding the token\
            \ can accept the invitation."
          example: inv_eyJhbGc...
        acceptLink:
          type: string
          description: "A ready-to-use accept link (your `acceptUrl` with the token\
            \ appended as a `t` query parameter). Returned only when you set `sendEmail`\
            \ to false, so you do not have to build the URL yourself when sending\
            \ your own email."
          example: https://my-app.com/accept?t=inv_eyJhbGc...
      description: Response returned when creating or resending a sub-user invitation.
    AccessProfileSpec:
      type: object
      properties:
        roleId:
          type: string
          description: "Identifier of an existing role in the same app context. The\
            \ invitee inherits that role's scopes. Provide this OR `scopes`, not both."
          example: engineering-member
        scopes:
          type: array
          description: "Inline list of scope clauses granted directly to the invitee.\
            \ Provide this OR `roleId`, not both."
          items:
            $ref: "#/components/schemas/ScopeClause"
      description: Permissions to grant on accept. Set exactly one of `roleId` or
        `scopes`.
    CreateInviteRequest:
      required:
      - accessProfile
      - contextId
      - email
      type: object
      properties:
        email:
          type: string
          description: "Email address to invite. Must be a valid email address. If\
            \ it already belongs to an active or suspended member of this app context\
            \ the request is rejected; if it already has a pending invitation in this\
            \ app context, that invitation is resent with a fresh token instead of\
            \ a new one being created."
          example: bob@example.com
        contextId:
          type: string
          description: The app context the new member will operate in. Must reference
            an existing app context in your account. The invitee's access profile
            is bound to this context.
          example: myapp
        accessProfile:
          $ref: "#/components/schemas/AccessProfileSpec"
        firstName:
          type: string
          description: Invitee's first name. Optional; used to personalize the invitation
            email greeting.
          example: Bob
        lastName:
          type: string
          description: Invitee's last name. Optional; accepted but not currently stored
            or returned.
          example: Wright
        acceptUrl:
          type: string
          description: Your landing page where the invitee accepts the invitation.
            Required when `sendEmail` is true. The invitation token is appended as
            a `t` query parameter. Must be an https URL with a domain-name host (IP-literal
            hosts are rejected).
          example: https://my-app.com/accept
        fromName:
          type: string
          description: Display name shown in the From header of the invitation email.
            Defaults to `Vectros`. Maximum 100 characters; must not contain newlines.
          example: Acme Team
          default: Vectros
        ttlSeconds:
          type: integer
          description: "How long the invitation token stays valid, in seconds. Defaults\
            \ to 604800 (7 days). Must be between 3600 (1 hour) and 2592000 (30 days)."
          format: int64
          example: 604800
          default: 604800
        sendEmail:
          type: boolean
          description: "Whether Vectros should deliver the invitation email for you.\
            \ When true (the default), the email is sent on your behalf. When false,\
            \ no email is sent and the response instead returns the raw token and\
            \ a ready-to-use accept link so you can deliver the invitation through\
            \ your own email provider."
          example: true
          default: true
      description: Request body for inviting a sub-user to one of your app contexts.
    ChatStreamEvent:
      type: object
      description: A single Server-Sent Event from `POST /v1/chat`. The `type` field
        discriminates which event type this is (the `event` field carries the same
        value for backward compatibility).
      discriminator:
        propertyName: type
      oneOf:
      - $ref: "#/components/schemas/ContentDeltaEvent"
      - $ref: "#/components/schemas/DoneEvent"
      - $ref: "#/components/schemas/ErrorEvent"
    ContentDeltaEvent:
      required:
      - delta
      - event
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `content_delta`."
          enum:
          - content_delta
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `content_delta`.
          enum:
          - content_delta
        delta:
          type: string
          description: The next chunk of generated text.
      description: A streamed chunk of generated model output. Concatenate the `delta`
        values in arrival order to reconstruct the full response.
    DoneEvent:
      required:
      - event
      - inferenceBalanceCentsCharged
      - inputTokens
      - model
      - outputTokens
      - platformCreditsCharged
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `done`."
          enum:
          - done
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `done`.
          enum:
          - done
        inputTokens:
          type: integer
          description: "Total input tokens consumed by the model, including the system\
            \ prompt and all messages."
          format: int32
        outputTokens:
          type: integer
          description: Total output tokens generated by the model.
          format: int32
        model:
          type: string
          description: The model alias used for this call.
        platformCreditsCharged:
          type: number
          description: Credits debited from your monthly plan allowance for this call's
            per-call platform overhead. One credit equals one cent.
          format: double
        inferenceBalanceCentsCharged:
          type: integer
          description: Cents debited from your pre-paid inference balance for the
            model's token cost.
          format: int64
        cacheReadTokens:
          type: integer
          description: Number of input tokens served from the prompt cache. Present
            only when prompt caching applied to this call. Prompt caching is not yet
            used; this field is reserved for forward compatibility.
          format: int32
        cacheCreateTokens:
          type: integer
          description: Number of tokens written to the prompt cache. Present only
            when prompt caching applied to this call. Prompt caching is not yet used;
            this field is reserved for forward compatibility.
          format: int32
      description: Terminal event emitted after the model finishes or the stream ends
        cleanly. Reports total token usage and the billing breakdown for this call.
    ErrorEvent:
      required:
      - code
      - event
      - message
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `error`."
          enum:
          - error
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `error`.
          enum:
          - error
        message:
          type: string
          description: "A human-readable description of the error, suitable for surfacing\
            \ to end users."
        code:
          type: string
          description: "A stable, machine-readable error code (for example, `inference_error`)."
      description: Emitted when an error occurs after the SSE stream has already opened.
        Errors that occur before streaming begins return a standard HTTP error response
        instead.
    ChatMessage:
      required:
      - content
      - role
      type: object
      properties:
        role:
          type: string
          description: The role of this message. `system` is extracted and used as
            the system prompt; `user` and `assistant` are sent as conversation turns.
          enum:
          - system
          - user
          - assistant
        content:
          type: string
          description: The text content of the message.
      description: "The conversation history, in order. A message with role `system`\
        \ is extracted and used as the system prompt; `user` and `assistant` messages\
        \ are sent to the model as conversation turns."
    ChatRequest:
      required:
      - messages
      type: object
      properties:
        messages:
          type: array
          description: "The conversation history, in order. A message with role `system`\
            \ is extracted and used as the system prompt; `user` and `assistant` messages\
            \ are sent to the model as conversation turns."
          items:
            $ref: "#/components/schemas/ChatMessage"
        model:
          type: string
          description: "Model alias to use, from the list returned by `GET /v1/models`.\
            \ Defaults to `claude-haiku-4-5`."
        maxTokens:
          type: integer
          description: Maximum number of tokens to generate. Defaults to 2048; the
            maximum is 8192.
          format: int32
        temperature:
          type: number
          description: "Sampling temperature, between 0.0 and 1.0. Higher values produce\
            \ more varied output. Defaults to 0.7."
          format: float
        topP:
          type: number
          description: Top-p (nucleus) sampling. Ignored when `temperature` is greater
            than 0.
          format: float
        allowGlobalRegion:
          type: boolean
          description: "Opt this request into global (non-US) region serving for lower\
            \ cost. Requires a signed global-processing waiver on your account that\
            \ permits per-request override; otherwise the request is rejected with\
            \ 403. When omitted, the request follows your account's default residency\
            \ setting. Configure data residency under Data Residency and Region settings\
            \ in the developer portal."
          example: false
      description: Request body for `POST /v1/chat`.
    DocumentAskStreamEvent:
      type: object
      description: "A single Server-Sent Event from `POST /v1/documents/{id}/ask`.\
        \ The `type` field discriminates which event type this is (the `event` field\
        \ carries the same value for backward compatibility)."
      discriminator:
        propertyName: type
      oneOf:
      - $ref: "#/components/schemas/DocumentContextEvent"
      - $ref: "#/components/schemas/ContentDeltaEvent"
      - $ref: "#/components/schemas/DoneEvent"
      - $ref: "#/components/schemas/ErrorEvent"
    DocumentContextEvent:
      required:
      - documentId
      - event
      - model
      - textBytes
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `document_context`."
          enum:
          - document_context
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `document_context`.
          enum:
          - document_context
        documentId:
          type: string
          description: The identifier of the document whose text was loaded as context.
        title:
          type: string
          description: "The display title you supplied for the document, if any."
        textBytes:
          type: integer
          description: "The size of the extracted document text, in bytes."
          format: int64
        model:
          type: string
          description: The model alias used to answer this request.
      description: "Emitted by `POST /v1/documents/{id}/ask` before the first `content_delta`,\
        \ describing the document loaded as context."
    DocumentAskRequest:
      required:
      - prompt
      type: object
      properties:
        prompt:
          type: string
          description: The question or instruction to answer about the document.
        instructions:
          type: string
          description: Optional system prompt that overrides the default. Defaults
            to a generic instruction to act as a helpful document analyst.
        model:
          type: string
          description: "Model alias to use, from the list returned by `GET /v1/models`.\
            \ Defaults to `claude-haiku-4-5`."
        maxTokens:
          type: integer
          description: Maximum number of tokens to generate. Defaults to 2048; the
            maximum is 8192.
          format: int32
        temperature:
          type: number
          description: Sampling temperature. Defaults to 0.2.
          format: float
        allowGlobalRegion:
          type: boolean
          description: "Opt this request into global (non-US) region serving for lower\
            \ cost. Requires a signed global-processing waiver on your account that\
            \ permits per-request override; otherwise the request is rejected with\
            \ 403. When omitted, the request follows your account's default residency\
            \ setting."
          example: false
      description: "Request body for `POST /v1/documents/{id}/ask`."
    RagSearchResult:
      required:
      - documentId
      - score
      type: object
      properties:
        documentId:
          type: string
          description: The stable identifier of the matched result (a document or
            a record).
        score:
          type: number
          description: The combined relevance score from hybrid ranking. Higher is
            more relevant.
          format: double
        textScore:
          type: number
          description: "The keyword (BM25) relevance score, from 0 to 1. Null when\
            \ this match came from semantic search only."
          format: double
        semanticScore:
          type: number
          description: "The cosine-similarity score from semantic (vector) search,\
            \ from 0 to 1. Null when this match came from keyword search only."
          format: double
        chunkText:
          type: string
          description: "The specific chunk of text that matched the query — the narrowest\
            \ snippet, useful for citation."
        contextText:
          type: string
          description: The wider surrounding passage supplied to the model as context
            — broader than `chunkText` to better ground the answer.
        snippet:
          type: string
          description: A display-ready snippet with the matched query terms emphasized
            — suitable for search-result UIs.
        metadata:
          type: object
          additionalProperties: true
          description: "Metadata you supplied on the source document, such as its\
            \ title, folder id, or custom tags."
        createdAt:
          type: string
          description: "The result's creation timestamp, as an ISO-8601 UTC timestamp."
          format: date-time
      description: A single result used to ground the answer.
    RagStreamEvent:
      type: object
      description: A single Server-Sent Event from `POST /v1/rag`. The `type` field
        discriminates which event type this is (the `event` field carries the same
        value for backward compatibility).
      discriminator:
        propertyName: type
      oneOf:
      - $ref: "#/components/schemas/SearchResultsEvent"
      - $ref: "#/components/schemas/TruncationWarningEvent"
      - $ref: "#/components/schemas/ContentDeltaEvent"
      - $ref: "#/components/schemas/DoneEvent"
      - $ref: "#/components/schemas/ErrorEvent"
    SearchResultsEvent:
      required:
      - event
      - results
      - searchTimeMs
      - totalResults
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `search_results`."
          enum:
          - search_results
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `search_results`.
          enum:
          - search_results
        results:
          type: array
          description: The ranked results used to ground the model's answer. An empty
            array means nothing matched.
          items:
            $ref: "#/components/schemas/RagSearchResult"
        totalResults:
          type: integer
          description: "The total number of matches found, which may exceed the number\
            \ of `results` returned when a result limit is applied."
          format: int32
        searchTimeMs:
          type: integer
          description: "Wall-clock duration of the retrieval step, in milliseconds."
          format: int64
        degraded:
          type: boolean
          description: "True when one search backend was unavailable and these results\
            \ came from the remaining engine only (for example, keyword search degraded\
            \ to semantic-only during an outage). The grounding context may be less\
            \ complete in this case."
        degradedLegs:
          type: array
          description: "Which retrieval legs were unavailable: `text` (keyword) and/or\
            \ `vector` (semantic). Empty when retrieval was not degraded."
          items:
            type: string
            description: "Which retrieval legs were unavailable: `text` (keyword)\
              \ and/or `vector` (semantic). Empty when retrieval was not degraded."
      description: "Emitted by `POST /v1/rag` once the retrieval step completes, before\
        \ the answer streams. Reports the results that ground the answer."
    TruncationWarningEvent:
      required:
      - event
      - reason
      - resultsRequested
      - resultsUsed
      - type
      type: object
      properties:
        type:
          type: string
          description: "Discriminator: identifies this event type. Always `truncation_warning`."
          enum:
          - truncation_warning
        event:
          type: string
          description: Backward-compatible alias of `type`. Always `truncation_warning`.
          enum:
          - truncation_warning
        resultsRequested:
          type: integer
          description: The number of results retrieved before truncation.
          format: int32
        resultsUsed:
          type: integer
          description: The number of results actually included in the prompt after
            dropping lower-scoring overflow.
          format: int32
        reason:
          type: string
          description: Why truncation occurred. Currently only `context_window_budget`
            (the results did not fit the model's context window).
          enum:
          - context_window_budget
      description: "Emitted by `POST /v1/rag` when the retrieved results exceed the\
        \ model's context window. Lower-scoring results are dropped before the prompt\
        \ is built, so the answer is grounded on fewer results than were retrieved."
    RagRequest:
      required:
      - query
      type: object
      properties:
        query:
          type: string
          description: The natural-language question to answer over your indexed content.
        instructions:
          type: string
          description: Optional system prompt that overrides the default. Defaults
            to a generic instruction to answer using only the provided context.
        model:
          type: string
          description: "Model alias to use, from the list returned by `GET /v1/models`.\
            \ Defaults to `claude-haiku-4-5`."
        search:
          $ref: "#/components/schemas/RagSearch"
        maxTokens:
          type: integer
          description: Maximum number of tokens to generate. Defaults to 1024; the
            maximum is 4096.
          format: int32
        temperature:
          type: number
          description: "Sampling temperature. Defaults to 0.3, favoring more deterministic,\
            \ retrieval-grounded answers."
          format: float
        allowGlobalRegion:
          type: boolean
          description: "Opt this request into global (non-US) region serving for lower\
            \ cost. Requires a signed global-processing waiver on your account that\
            \ permits per-request override; otherwise the request is rejected with\
            \ 403. When omitted, the request follows your account's default residency\
            \ setting."
          example: false
      description: Request body for `POST /v1/rag`.
    RagSearch:
      type: object
      properties:
        mode:
          type: string
          description: "Retrieval mode: `HYBRID` combines keyword and semantic search,\
            \ `SEMANTIC` uses vector similarity only, `TEXT` uses keyword (BM25) matching\
            \ only. Defaults to `HYBRID`."
          enum:
          - HYBRID
          - SEMANTIC
          - TEXT
        limit:
          type: integer
          description: Maximum number of results to retrieve before building the prompt.
            Defaults to 10; the maximum is 50.
          format: int32
        filters:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/FilterValue'
          description: "Field filters to apply to your indexed content, as a map of\
            \ field name to required value."
        userId:
          type: string
          description: Restrict retrieval to content owned by this user id.
        orgId:
          type: string
          description: Restrict retrieval to content owned by this organization id.
        clientId:
          type: string
          description: Restrict retrieval to content tagged with this client id.
        contentTypes:
          type: array
          description: "Narrow retrieval to specific content types. When omitted,\
            \ both documents and records are searched — records are valid grounding\
            \ content too (for example, structured patient data or intake forms).\
            \ Pass [\"documents\"] for documents only or [\"records\"] for records\
            \ only. Each retrieved result carries a `sourceType` field indicating\
            \ which type it is."
          example:
          - documents
          - records
          items:
            type: string
            description: "Narrow retrieval to specific content types. When omitted,\
              \ both documents and records are searched — records are valid grounding\
              \ content too (for example, structured patient data or intake forms).\
              \ Pass [\"documents\"] for documents only or [\"records\"] for records\
              \ only. Each retrieved result carries a `sourceType` field indicating\
              \ which type it is."
            example: "[\"documents\",\"records\"]"
            enum:
            - documents
            - records
          enum:
          - documents
          - records
        folderId:
          type: string
          description: "Restrict retrieval to content (documents or records) in this\
            \ exact folder. Folders are a unified organizational primitive and may\
            \ hold mixed content. Provide the UUID of a folder. To include a folder\
            \ and all of its descendants instead, use `rootFolderId`."
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        rootFolderId:
          type: string
          description: Restrict retrieval to content under this subtree — the folder
            and all of its descendants. Provide the UUID of a top-level (root) folder.
            Applies uniformly to documents and records. Use `folderId` to scope to
            a single folder exactly.
          example: f47ac10b-58cc-4372-a567-0e02b2c3d479
        typeName:
          type: string
          description: "Restrict retrieval to records of a specific schema type. Applies\
            \ only to records: setting it implicitly limits results to records unless\
            \ `contentTypes` also includes `documents`, in which case documents are\
            \ still returned unfiltered."
          example: patient
        createdAfter:
          type: string
          description: "Restrict retrieval to content created at or after this ISO-8601\
            \ UTC timestamp. Useful for incremental retrieval (for example, answering\
            \ based only on content ingested today). Pair with `createdBefore` to\
            \ define a time window."
          example: 2026-05-27T14:00:00Z
        createdBefore:
          type: string
          description: "Restrict retrieval to content created at or before this ISO-8601\
            \ UTC timestamp. Pair with `createdAfter` to define a time window, or\
            \ use alone to scope retrieval to content older than a cutoff."
          example: 2026-05-27T15:00:00Z
        requireComplete:
          type: boolean
          description: "Require complete retrieval. When true, if a search backend\
            \ is unavailable the request returns HTTP 503 before any answer is streamed,\
            \ rather than grounding the answer on partial results. Defaults to false,\
            \ in which case an outage degrades to the surviving search engine and\
            \ the `search_results` event reports `degraded: true`. Set this to true\
            \ when an answer grounded on incomplete retrieval is unacceptable."
          example: false
      description: "Configuration for the retrieval step: search mode, result limit,\
        \ filters, and ownership/folder scope."
