> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# delete_tenant

> Disable the tenant and keep its data (default), or with ``purge=true`` delete all of it. Cost class: write.

A disabled tenant is re-enabled with ``PUT`` and ``disabled: false``. A purge cannot be undone: the
tenant is disabled at once, its jobs are cancelled, and every workspace, file, result, cache entry and
key is deleted. Only the content-free billing record (job ids, kinds, timestamps, units, credits) is
kept. A purge already queued or running is returned as is; one that failed or ended unverified is
started again. Once a purge has verified clean the ``external_ref`` no longer resolves, so a repeat of
this call answers ``tenant_not_found`` and a later ``PUT`` creates a new, empty tenant.



## OpenAPI

````yaml /openapi.json delete /v1/tenants/{external_ref}
openapi: 3.1.0
info:
  description: >-
    Document intelligence: parse, split, classify, extract, and ground files, or
    build a searchable workspace. Authenticate with ``X-API-Key``.
  title: NDI Platform API
  version: 0.1.0
servers:
  - url: https://ndi-api.nace.ai
security: []
tags: []
paths:
  /v1/tenants/{external_ref}:
    delete:
      tags:
        - tenants
      summary: delete_tenant
      description: >-
        Disable the tenant and keep its data (default), or with ``purge=true``
        delete all of it. Cost class: write.


        A disabled tenant is re-enabled with ``PUT`` and ``disabled: false``. A
        purge cannot be undone: the

        tenant is disabled at once, its jobs are cancelled, and every workspace,
        file, result, cache entry and

        key is deleted. Only the content-free billing record (job ids, kinds,
        timestamps, units, credits) is

        kept. A purge already queued or running is returned as is; one that
        failed or ended unverified is

        started again. Once a purge has verified clean the ``external_ref`` no
        longer resolves, so a repeat of

        this call answers ``tenant_not_found`` and a later ``PUT`` creates a
        new, empty tenant.
      operationId: delete_tenant_v1_tenants__external_ref__delete
      parameters:
        - description: The partner's own reference for the tenant, such as its account id.
          in: path
          name: external_ref
          required: true
          schema:
            description: >-
              The partner's own reference for the tenant, such as its account
              id.
            maxLength: 128
            minLength: 1
            pattern: ^\S(.*\S)?$
            title: External Ref
            type: string
        - description: >-
            True permanently deletes the tenant's data instead of disabling it.
            Answers 202 with a `tenant_purge` job to poll on GET
            /v1/jobs/{job_id} without X-NDI-Tenant.
          in: query
          name: purge
          required: false
          schema:
            default: false
            description: >-
              True permanently deletes the tenant's data instead of disabling
              it. Answers 202 with a `tenant_purge` job to poll on GET
              /v1/jobs/{job_id} without X-NDI-Tenant.
            title: Purge
            type: boolean
      responses:
        '200':
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/Tenant'
                  - $ref: '#/components/schemas/TenantPurgeJob'
                title: Response Delete Tenant V1 Tenants  External Ref  Delete
          description: Successful Response
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TenantPurgeJob'
          description: The purge was accepted, or is already running.
        '504':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Gateway Timeout
        default:
          content:
            application/json:
              example:
                error:
                  code: invalid_request
                  detail: null
                  message: >-
                    Request body has extra fields that this operation does not
                    accept.
                  request_id: req-01j9k2n3p4q5r6s7t8v9
                  retryable: false
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: >-
            Typed error envelope used by every 4xx and 5xx. ``error.code``
            distinguishes the failure; HTTP status is a consequence of the code.
      security:
        - APIKeyHeader: []
components:
  schemas:
    Tenant:
      additionalProperties: false
      description: >-
        One of the partner's child tenants. Send ``X-NDI-Tenant:
        ext:<external_ref>`` to act for it.
      properties:
        created_at:
          description: When the tenant was created.
          format: date-time
          title: Created At
          type: string
        data_retention:
          $ref: '#/components/schemas/GranularRetention'
          description: >-
            The data-retention setting in force for the tenant. A stored
            zero_retention reads as short while this deployment does not offer
            zero retention.
        disabled:
          description: Whether the tenant is disabled. Its data is kept.
          title: Disabled
          type: boolean
        display_name:
          anyOf:
            - type: string
            - type: 'null'
          description: Human-readable name of the tenant.
          title: Display Name
        external_ref:
          description: The partner's own reference for the tenant, unique per partner.
          title: External Ref
          type: string
        id:
          description: NDI's id for the tenant; X-NDI-Tenant accepts it too.
          format: uuid
          title: Id
          type: string
        limits:
          $ref: '#/components/schemas/TenantLimits'
          description: The limits in force for the tenant.
        retention_tier:
          $ref: '#/components/schemas/RetentionTier'
          description: >-
            The tenant's account tier for job retention: a paid tenant's job
            inputs, outputs and processing files are removed 48 hours after the
            job finishes, a free tenant's 7 days after. A failed or cancelled
            job counts from the failure or cancel.
      required:
        - id
        - external_ref
        - display_name
        - disabled
        - limits
        - data_retention
        - retention_tier
        - created_at
      title: Tenant
      type: object
    TenantPurgeJob:
      additionalProperties: false
      properties:
        cost:
          anyOf:
            - $ref: '#/components/schemas/JobCost'
            - type: 'null'
          description: >-
            Final USD component prices under the sheet pinned at acceptance;
            null before finalization or when no dollar sheet was configured.
            Does not change legacy credit billing.
        created_at:
          description: When the job row was created.
          format: date-time
          title: Created At
          type: string
        credit_schedule_version:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Version of the credit schedule `credits` were priced under. Null
            until `usage_final` is true.
          title: Credit Schedule Version
        credits:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Final credits billed for this job, priced once under
            `credit_schedule_version`. Null until `usage_final` is true. Never
            changes afterwards, including after payload expiry or deletion. For
            a job that failed or was cancelled, the credits for results already
            delivered before it ended (a progressive classify or extract
            publishes before it finishes); 0 when it delivered nothing. Always
            null on a stage job that Automatic Search delegated: its work is
            billed on the parent job.
          title: Credits
        deleted_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: >-
            When the caller hid this run from listings, via `delete_job`. A soft
            delete: the job still reads by id and still counts toward `GET
            /v1/usage` — what it stops doing is appearing in `GET /v1/jobs`.
          title: Deleted At
        effort:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            How hard this run worked, for the search kinds that carry it. A
            deep-search job (kind deep_search_v2) created before the effort knob
            was removed echoes its requested level: 'low', 'medium', or 'high';
            newer deep-search jobs, which all run one configuration, read null.
            An intelligent_search row carries an internal selector instead:
            'fast' is fact-search, 'automatic' is automatic-search, and 'deep'
            (or the legacy 'balanced') is a deep-search or console QA chat job
            created before the 2026-09 consolidation — those two are not
            distinguishable by this field. Null for every other kind. Read from
            the stored request, so it rides the same retention path as `query`
            and `result`: null once `result_state` leaves `available`.
          title: Effort
        error:
          anyOf:
            - $ref: '#/components/schemas/Error'
            - type: 'null'
          description: >-
            Typed failure envelope when status is failed. Null on any other
            status.
        finished_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: When the job reached a terminal status.
          title: Finished At
        force:
          anyOf:
            - type: boolean
            - type: 'null'
          description: >-
            The request's own `force` flag, echoed regardless of outcome.
            Present for kinds whose request carries one (e.g. kg_build, where a
            true value cleared the durable store before the job ran) so a caller
            can tell that apart from a build that failed leaving the prior store
            untouched — `result` alone cannot, since it is absent on failure
            either way.
          title: Force
        idempotency_key:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Key that was replayed to return this job. Absent when the create
            call had no Idempotency-Key.
          title: Idempotency Key
        job_id:
          description: >-
            Server-minted id. Poll GET /v1/jobs/{job_id} or pass wait_seconds on
            create.
          format: uuid
          title: Job Id
          type: string
        kind:
          const: tenant_purge
          default: tenant_purge
          title: Kind
          type: string
        name:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Display name (the console's 'project'). Public Parse, Split,
            Classify, Extract, and Ground runs supply a stable server default
            when blank; citation-generated Ground jobs are excluded. mutable via
            PATCH /v1/jobs/{job_id}.
          title: Name
        payload_expires_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: >-
            When the job's inputs, outputs and processing files are removed.
            Once the job finishes this is no later than 48 hours after it on a
            paid account and 7 days after on a free one, or sooner if the
            workspace's `job_ttl_days` says so. Null while a job with no
            `job_ttl_days` bound is still running, or when the account keeps no
            job content (zero retention).
          title: Payload Expires At
        progress:
          anyOf:
            - $ref: '#/components/schemas/JobProgress'
            - type: 'null'
          description: >-
            Live progress while the job is running. Null while queued and after
            the job finishes.
        project_id:
          anyOf:
            - format: uuid
              type: string
            - type: 'null'
          description: >-
            Caller-minted grouping id shared by jobs submitted together.
            Filterable on GET /v1/jobs.
          title: Project Id
        query:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            The question this job was asked, for the search kinds that carry one
            (intelligent_search, qa_file, qa_tables, filtered_search); null for
            every other kind. Read from the stored request, so it rides the same
            retention path as `result`: null once `result_state` leaves
            `available`. Narrow by design — the frozen access context never
            reaches the wire. File scope is summarised separately as
            `query_scope`.
          title: Query
        query_scope:
          anyOf:
            - discriminator:
                mapping:
                  file: '#/components/schemas/FileQueryScope'
                  files: '#/components/schemas/FilesQueryScope'
                  path_prefix: '#/components/schemas/PathPrefixQueryScope'
                  workspace: '#/components/schemas/WorkspaceQueryScope'
                propertyName: type
              oneOf:
                - $ref: '#/components/schemas/WorkspaceQueryScope'
                - $ref: '#/components/schemas/FileQueryScope'
                - $ref: '#/components/schemas/PathPrefixQueryScope'
                - $ref: '#/components/schemas/FilesQueryScope'
            - type: 'null'
          description: >-
            Where this search ran, summarised for history UIs: the whole
            workspace, one file path, one folder prefix, or a count of files.
            Null for non-query jobs, filtered-search page continuations, and
            once `result_state` leaves `available`. Never lists multiple
            filenames, never the raw request, and never the frozen API-key id.
          title: Query Scope
        result:
          anyOf:
            - $ref: '#/components/schemas/TenantPurgeResult'
            - type: 'null'
          description: Present once the purge has finished.
        result_state:
          $ref: '#/components/schemas/ResultState'
          default: available
          description: >-
            Why result may be null. available with a null result means the job
            has not succeeded yet; expired and not_retained mean the payload was
            dropped by retention; cancelled means the job, or the job it ran
            for, was cancelled, so no result is served (a cancelled job is
            charged only for results delivered before the cancel).
        source:
          anyOf:
            - discriminator:
                mapping:
                  parse_result: '#/components/schemas/ParseResultSource'
                  upload: '#/components/schemas/UploadSource'
                  url: '#/components/schemas/UrlSource'
                  workspace_file: '#/components/schemas/WorkspaceFileSource'
                propertyName: type
              oneOf:
                - $ref: '#/components/schemas/UploadSource'
                - $ref: '#/components/schemas/UrlSource'
                - $ref: '#/components/schemas/WorkspaceFileSource'
                - $ref: '#/components/schemas/ParseResultSource'
            - type: 'null'
          description: >-
            What this job read, for the document operations that take a single
            `source` (parse, split, classify, extract, ground); null for every
            other kind. Together with `workspace_id` this is what lets a caller
            re-open the document a past run was about — a `workspace_file`
            source stays readable after the run, while an `upload` is single-use
            and its bytes are gone. Read from the stored request, so it rides
            the same retention path as `query` and `result`: null once
            `result_state` leaves `available`.
          title: Source
        started_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: When execution began. Null while still queued.
          title: Started At
        status:
          $ref: >-
            #/components/schemas/ndi_service__app__platform_api__contracts__JobStatus
          description: 'Lifecycle: queued, running, succeeded, failed, or cancelled.'
        surface:
          anyOf:
            - enum:
                - deep_search
                - qa
                - workbook
              type: string
            - type: 'null'
          description: >-
            Which console history this deep-search job belongs to. `deep_search`
            is the public route and the Deep Search page; `qa` and `workbook`
            are the console chat skins over the same agent. It does not pick the
            implementation. Null for fact-search and every non-deep-search kind.
            Jobs stored before this field existed echo `deep_search`. Read from
            the stored payload, so it rides the same retention path as `query`.
          title: Surface
        units:
          default: 0
          description: >-
            Billable units once the job finishes; 0 while queued or running.
            Kept for compatibility: `credits` is the authoritative billed
            amount.
          title: Units
          type: integer
        usage:
          anyOf:
            - $ref: '#/components/schemas/JobUsage'
            - type: 'null'
          description: >-
            What `credits` were charged for: one line per part (the parse, the
            tool's own work), with the quantity, rate and multiplier behind
            each. Null until `usage_final` is true, and on jobs finalized before
            usage breakdowns were introduced.
        usage_final:
          default: false
          description: >-
            True once `credits` is final and will not change. False while the
            job is queued or running, and until any cleanup of its delegated
            stages has been accounted for. A failed tool job (read_file,
            run_sql, hybrid_search, grep, file_metadata, folder_metadata)
            created with an Idempotency-Key stays false for one hour after it
            fails, because a retry with the same key re-runs it; after that hour
            it is final and the retry returns the stored error.
          title: Usage Final
          type: boolean
        usage_finalized_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: When `credits` became final. Null until `usage_final` is true.
          title: Usage Finalized At
        workspace_id:
          anyOf:
            - format: uuid
              type: string
            - type: 'null'
          description: >-
            The workspace this job reads. Null for a document operation whose
            source is an upload, a URL, or a prior parse result.
          title: Workspace Id
      required:
        - job_id
        - status
        - created_at
      title: TenantPurgeJob
      type: object
    ErrorEnvelope:
      description: Every non-2xx body on ``/v1``.
      example:
        error:
          code: invalid_request
          detail: null
          message: Request body has extra fields that this operation does not accept.
          request_id: req-01j9k2n3p4q5r6s7t8v9
          retryable: false
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
      title: ErrorEnvelope
      type: object
    GranularRetention:
      description: >-
        Retention for document operations — a **client** setting, not a request
        field.


        A data-handling guarantee a caller can accidentally opt out of on one

        endpoint is not a guarantee, so this is configured per client at
        onboarding

        and appears in no request body.
      enum:
        - short
        - zero_retention
      title: GranularRetention
      type: string
    TenantLimits:
      additionalProperties: false
      description: The limits in force for a tenant.
      properties:
        max_concurrent_jobs:
          description: >-
            Parse, split, classify, categorize, extract, ground and agent jobs
            this tenant may have running at once; more wait in the queue. NDI's
            own per-tenant cap applies when it is lower.
          title: Max Concurrent Jobs
          type: integer
        rpm:
          description: Requests per minute this tenant may make.
          title: Rpm
          type: integer
      required:
        - rpm
        - max_concurrent_jobs
      title: TenantLimits
      type: object
    RetentionTier:
      description: >-
        How long a client's finished jobs keep their content — a **client**
        setting, not a request field.


        ``paid`` keeps it ``PAID_JOB_RETENTION`` after the job finishes,
        ``free``

        ``FREE_JOB_RETENTION`` (``app/platform_api/limits.py``). A client with
        no

        stored tier resolves from its row (``app/db/models/client.py``
        ``on_free_retention_tier``).
      enum:
        - paid
        - free
      title: RetentionTier
      type: string
    JobCost:
      additionalProperties: false
      description: An immutable USD price independent of the legacy credit schedule.
      properties:
        currency:
          const: USD
          default: USD
          title: Currency
          type: string
        lines:
          description: >-
            The independently priced Parse and operation components, including
            paid reuse.
          items:
            $ref: '#/components/schemas/CostLine'
          title: Lines
          type: array
        pricing_version:
          description: The dollar price-sheet version pinned when this job was accepted.
          title: Pricing Version
          type: string
        total_usd:
          description: Sum of the component amounts in USD, serialized as a decimal string.
          pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d{0,9}0*$
          title: Total Usd
          type: string
      required:
        - pricing_version
        - total_usd
        - lines
      title: JobCost
      type: object
    Error:
      description: The error object, per spec 12 §1.8.
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        detail:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          description: Structured payload, when the code carries one.
          title: Detail
        message:
          description: Human- and model-readable; says what to do differently.
          title: Message
          type: string
        request_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Per-request correlation id; matches the ``X-Request-Id`` header.
            Null only when the error was minted outside any HTTP request, e.g.
            by a worker.
          title: Request Id
        retryable:
          description: Whether retrying the identical request could succeed.
          title: Retryable
          type: boolean
      required:
        - code
        - message
        - retryable
      title: Error
      type: object
    JobProgress:
      additionalProperties: false
      description: Absent until the job is ``running``.
      properties:
        items_done:
          default: 0
          title: Items Done
          type: integer
        items_failed:
          default: 0
          description: >-
            Quarantined after exhausting retries, for batch kinds. Excluded from
            items_done.
          title: Items Failed
          type: integer
        items_total:
          anyOf:
            - type: integer
            - type: 'null'
          description: Files, for batch kinds.
          title: Items Total
        knowledge_graph:
          anyOf:
            - $ref: '#/components/schemas/KnowledgeGraphBuildProgress'
            - type: 'null'
          description: Detail only a kg_build reports; null for every other job kind.
        routing:
          anyOf:
            - $ref: '#/components/schemas/AutomaticSearchRouting'
            - type: 'null'
          description: >-
            The route an automatic search chose, from the moment it chose it, so
            a caller can show the decision while the chosen pipeline runs;
            timings arrive with the result's ``routing``. Null for every other
            job kind.
        stage:
          anyOf:
            - type: string
            - type: 'null'
          description: Current pipeline stage, for display only.
          title: Stage
        units_done:
          default: 0
          title: Units Done
          type: integer
        units_total:
          anyOf:
            - type: integer
            - type: 'null'
          title: Units Total
      title: JobProgress
      type: object
    FileQueryScope:
      additionalProperties: false
      description: The question ran against one workspace-relative file.
      properties:
        path:
          maxLength: 1024
          minLength: 1
          title: Path
          type: string
        type:
          const: file
          default: file
          title: Type
          type: string
      required:
        - path
      title: FileQueryScope
      type: object
    FilesQueryScope:
      additionalProperties: false
      description: The question ran against several files; only the count is public.
      properties:
        count:
          minimum: 2
          title: Count
          type: integer
        type:
          const: files
          default: files
          title: Type
          type: string
      required:
        - count
      title: FilesQueryScope
      type: object
    PathPrefixQueryScope:
      additionalProperties: false
      description: The question ran against one folder subtree.
      properties:
        prefix:
          maxLength: 1024
          minLength: 1
          title: Prefix
          type: string
        type:
          const: path_prefix
          default: path_prefix
          title: Type
          type: string
      required:
        - prefix
      title: PathPrefixQueryScope
      type: object
    WorkspaceQueryScope:
      additionalProperties: false
      description: The question ran against the whole workspace.
      properties:
        type:
          const: workspace
          default: workspace
          title: Type
          type: string
      title: WorkspaceQueryScope
      type: object
    TenantPurgeResult:
      additionalProperties: false
      description: >-
        The tenant's content is gone; no row is deleted: its billing record and
        metadata rows stay, de-identified.


        ``verified`` is true only when every store reports nothing remaining. An
        unfinished

        cancelled job counts against ``jobs`` and a workspace teardown that
        could not verify

        its own storage, index and graph counts against ``workspaces``. A purge
        that ends

        unverified is a ``succeeded`` job on purpose, like a workspace delete:
        the report says

        what is left, and a second DELETE re-runs it.
      properties:
        billing_record_kept:
          description: >-
            True when the content-free billing record (job ids, kinds,
            timestamps, units, credits) was kept.
          title: Billing Record Kept
          type: boolean
        external_ref:
          description: >-
            The partner's own reference the tenant had. It stops resolving once
            the purge succeeds.
          title: External Ref
          type: string
        result_type:
          const: tenant_purge
          default: tenant_purge
          title: Result Type
          type: string
        stores:
          description: One line per store, with what the verification pass still found.
          items:
            $ref: '#/components/schemas/TenantPurgeStoreReport'
          title: Stores
          type: array
        tenant_id:
          description: The tenant whose data was purged, as NDI identifies it.
          format: uuid
          title: Tenant Id
          type: string
        verified:
          description: >-
            True when no content of the tenant remains anywhere and every kept
            metadata row is de-identified.
          title: Verified
          type: boolean
      required:
        - tenant_id
        - external_ref
        - verified
        - stores
        - billing_record_kept
      title: TenantPurgeResult
      type: object
    ResultState:
      description: >-
        Why ``Job.result`` is null.


        ``available`` with a null result means the job has not succeeded yet;

        ``expired`` means retention dropped it; ``not_retained`` means the
        client is

        provisioned ``zero_retention`` and already had its one chance to read
        it;

        ``cancelled`` means the job, or the root job it ran for, was cancelled.
        A

        cancelled job serves no results (it is charged only for results
        delivered before the cancel) — not its

        own progressive values, and not a delegated child's result. It is a read

        state only, derived on the wire; no row stores it.

        A late poll returns the job with a state, never a 404 — "this expired"
        and

        "this never existed" are a scheduling problem and a bug respectively,
        and a

        404 makes them identical.
      enum:
        - available
        - expired
        - not_retained
        - cancelled
      title: ResultState
      type: string
    ParseResultSource:
      additionalProperties: false
      description: >-
        A prior Parse job whose parsed artifact can be reused by downstream
        methods.
      properties:
        job_id:
          description: Succeeded parse job whose artifact should be reused.
          format: uuid
          title: Job Id
          type: string
        type:
          const: parse_result
          default: parse_result
          title: Type
          type: string
      required:
        - job_id
      title: ParseResultSource
      type: object
    UploadSource:
      additionalProperties: false
      description: Bytes staged through ``POST /v1/uploads``. Single-use, TTL-bound.
      properties:
        type:
          const: upload
          default: upload
          title: Type
          type: string
        upload_id:
          description: >-
            Id returned by POST /v1/uploads. Single-use; bytes are deleted after
            the job.
          format: uuid
          title: Upload Id
          type: string
      required:
        - upload_id
      title: UploadSource
      type: object
    UrlSource:
      additionalProperties: false
      description: A public or presigned HTTPS location we fetch server-side.
      properties:
        file_name:
          description: Filename to record for the fetched bytes.
          maxLength: 512
          title: File Name
          type: string
        type:
          const: url
          default: url
          title: Type
          type: string
        url:
          description: HTTPS URL the service fetches.
          maxLength: 2048
          pattern: ^https://
          title: Url
          type: string
      required:
        - url
        - file_name
      title: UrlSource
      type: object
    WorkspaceFileSource:
      additionalProperties: false
      description: >-
        A file already in a workspace.


        The single seam between the two contexts: a granular operation may read
        a

        workspace file and still write nothing back. The file need **not** be

        ingested — granular operations read source bytes, not representations.
      properties:
        file_id:
          description: File id from GET /v1/workspaces/{workspace_id}/files.
          format: uuid
          title: File Id
          type: string
        type:
          const: workspace_file
          default: workspace_file
          title: Type
          type: string
        workspace_id:
          description: Workspace that owns the file.
          format: uuid
          title: Workspace Id
          type: string
      required:
        - workspace_id
        - file_id
      title: WorkspaceFileSource
      type: object
    ndi_service__app__platform_api__contracts__JobStatus:
      description: >-
        ``cancelled`` is a first-class terminal status, not a ``failed``
        variant.


        Conflating them makes error-rate metrics lie and forces every client to

        string-match an error code.
      enum:
        - queued
        - running
        - succeeded
        - failed
        - cancelled
      title: JobStatus
      type: string
    JobUsage:
      additionalProperties: false
      description: How a job's final credits were computed.
      properties:
        lines:
          description: >-
            One line per part. Their credits sum to the job's credits; waived
            parts remain listed at 0 credits.
          items:
            $ref: '#/components/schemas/UsageLine'
          title: Lines
          type: array
      required:
        - lines
      title: JobUsage
      type: object
    CostLine:
      additionalProperties: false
      description: One independently priced part of a perception job.
      properties:
        amount_usd:
          description: >-
            This line's USD amount: quantity multiplied by unit_price_usd,
            serialized as a decimal string.
          pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d{0,9}0*$
          title: Amount Usd
          type: string
        execution:
          description: >-
            The price tier: normal work or verified, billing-eligible tenant
            reuse; not provider token caching.
          enum:
            - fresh
            - reused
          title: Execution
          type: string
        operation:
          description: The work this dollar amount pays for.
          enum:
            - parse
            - split
            - classify
            - extract
            - ground
          title: Operation
          type: string
        quantity:
          description: >-
            The number of page-equivalents or Ground targets priced by this
            line.
          minimum: 0
          title: Quantity
          type: integer
        reuse_reason:
          anyOf:
            - enum:
                - reused_parse
                - repeat_of_job
              type: string
            - type: 'null'
          description: Why the lower reuse rate applies; null on a normal-price line.
          title: Reuse Reason
        unit:
          description: 'The quantity''s billing unit: page-equivalent or Ground target.'
          enum:
            - page
            - item
          title: Unit
          type: string
        unit_price_usd:
          description: >-
            USD per unit under the job's pinned price sheet, serialized as a
            decimal string.
          pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d{0,9}0*$
          title: Unit Price Usd
          type: string
      required:
        - operation
        - quantity
        - unit
        - execution
        - unit_price_usd
        - amount_usd
      title: CostLine
      type: object
    ErrorCode:
      description: |-
        Every error the ``/v1`` surface can return.

        Clients must treat this as an **open** enum: a new member is an additive
        change (spec 12 §9), and a client that crashes on an unknown code makes
        every future error a breaking change.
      enum:
        - invalid_request
        - unsupported_file_type
        - file_too_large
        - invalid_schema
        - invalid_sql
        - invalid_path_prefix
        - command_not_permitted
        - batch_too_large
        - domain_validation_failed
        - default_label_required
        - wait_required_for_zero_retention
        - ledger_decision_invalid
        - tenant_required
        - partner_required
        - workspace_not_found
        - file_not_found
        - job_not_found
        - upload_not_found
        - node_not_found
        - schema_not_found
        - domain_not_found
        - session_not_found
        - access_label_not_found
        - ledger_understanding_not_found
        - ledger_table_not_found
        - tenant_not_found
        - path_conflict
        - schema_version_conflict
        - access_label_name_taken
        - workspace_name_taken
        - access_label_in_use
        - upload_expired
        - file_not_ingested
        - workspace_deleting
        - workspace_not_deletable
        - ingestion_in_progress
        - job_not_cancellable
        - confirmation_required
        - domain_not_published
        - domain_in_use
        - reserved_domain_slug
        - kg_not_built
        - kg_build_in_progress
        - ledger_understanding_in_progress
        - ledger_understanding_version_outdated
        - session_busy
        - tenant_limit_reached
        - range_not_satisfiable
        - result_expired
        - access_denied
        - tenant_disabled
        - access_backstop_violation
        - access_mode_not_implemented
        - rate_limited
        - concurrency_limit_reached
        - quota_exceeded
        - corrupt_file
        - zero_byte_file
        - encrypted_file
        - csv_dialect_ambiguous
        - csv_parse_failed
        - parse_failed
        - ocr_failed
        - conversion_failed
        - sheetless_workbook
        - unplayable_media
        - unclassified_pages
        - job_cancelled
        - job_timeout
        - upstream_unavailable
        - internal_error
        - unauthorized
        - not_implemented
      title: ErrorCode
      type: string
    KnowledgeGraphBuildProgress:
      additionalProperties: false
      description: >-
        Where a ``kg_build`` is, beyond what ``stage`` alone can say.


        A graph build runs for minutes to hours through a fixed sequence of
        phases,

        and ``stage`` names only the current one. Turning that into "how far
        along"

        otherwise means hard-coding the build engine's phase order in every
        client —

        an order that lives in another package and is free to gain a phase. So
        the

        position is reported rather than inferred.


        The counters are deliberately not a percentage. The phases are wildly
        unequal

        (extraction is ~60% of a build, and its per-document cost varies 25x
        with how

        dense the documents are), so weighting them is a presentation decision,
        made

        against measurements, that belongs to whoever is drawing the bar.
      properties:
        countable:
          description: >-
            Whether items_total/items_done describe this phase. False for the
            phases that are a single step, which have no unit to count — draw
            those as indeterminate rather than as a bar stuck at zero.
          title: Countable
          type: boolean
        documents:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Documents registered from the corpus, once the registering phase has
            run.
          title: Documents
        mentions:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Mentions extracted across the corpus, once the validating phase has
            run.
          title: Mentions
        phase_count:
          description: >-
            How many phases a build passes through before it is done. Currently
            9.
          title: Phase Count
          type: integer
        phase_index:
          description: >-
            0-based position of the current phase in the build's fixed run
            order; equals phase_count when done.
          title: Phase Index
          type: integer
        step:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            The span in flight inside a single-step phase, reported by the
            running step itself. Null when the phase counts its own units, or
            before the step has reported one.
          title: Step
        step_done:
          anyOf:
            - type: integer
            - type: 'null'
          description: Units of `step` finished, when it has countable units.
          title: Step Done
        step_total:
          anyOf:
            - type: integer
            - type: 'null'
          description: Units `step` has to get through, when that is known.
          title: Step Total
      required:
        - phase_index
        - phase_count
        - countable
      title: KnowledgeGraphBuildProgress
      type: object
    AutomaticSearchRouting:
      additionalProperties: false
      properties:
        delegated_wall_ms:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Wall time spent preparing and running the native deep or filtered
            job.
          title: Delegated Wall Ms
        fact_wall_ms:
          anyOf:
            - type: integer
            - type: 'null'
          description: Wall time spent running fact search, when selected.
          title: Fact Wall Ms
        final:
          description: The pipeline that produced the returned output.
          enum:
            - fact
            - agent
            - deep
            - filtered
          title: Final
          type: string
        reason:
          description: The router's explanation of its decision, or the requested `mode`.
          title: Reason
          type: string
        router:
          description: >-
            Which router decided, and what it measured: an LLM (its explanation
            is ``reason``), NaceDM (its probability and decision point), or
            `requested` when the request's `mode` was `fact` or `agent` and no
            router ran. Results routed before this field existed read as the
            LLM.
          discriminator:
            mapping:
              llm: '#/components/schemas/AutomaticLlmRouter'
              nacedm: '#/components/schemas/AutomaticNaceDmRouter'
              requested: '#/components/schemas/AutomaticRequestedRouter'
            propertyName: kind
          oneOf:
            - $ref: '#/components/schemas/AutomaticLlmRouter'
            - $ref: '#/components/schemas/AutomaticNaceDmRouter'
            - $ref: '#/components/schemas/AutomaticRequestedRouter'
          title: Router
        router_model:
          description: >-
            The model used to choose the search pipeline; `none` when the
            request's `mode` named it.
          title: Router Model
          type: string
        router_wall_ms:
          anyOf:
            - type: integer
            - type: 'null'
          description: Wall time spent selecting the initial route.
          title: Router Wall Ms
        selected:
          description: The route the router chose, or the one the request's `mode` named.
          enum:
            - fact
            - agent
            - deep
            - filtered
          title: Selected
          type: string
      required:
        - selected
        - final
        - reason
        - router_model
      title: AutomaticSearchRouting
      type: object
    TenantPurgeStoreReport:
      additionalProperties: false
      description: What the verification pass still finds of the tenant in one store.
      properties:
        remaining:
          description: >-
            Rows that still hold the tenant's content or identifying data or
            still grant access, objects, and open executions still found there.
            Kept rows that are already emptied do not count. Zero when clean.
          minimum: 0
          title: Remaining
          type: integer
        store:
          $ref: '#/components/schemas/TenantPurgeStore'
          description: The store this line reports.
      required:
        - store
        - remaining
      title: TenantPurgeStoreReport
      type: object
    UsageLine:
      additionalProperties: false
      description: One part of a job's final credits.
      properties:
        credits:
          description: >-
            quantity x credits_per_unit x multiplier: what this part added to
            the job's credits. 0 when waived.
          minimum: 0
          title: Credits
          type: integer
        credits_per_unit:
          description: The credit schedule's rate for one unit of this part.
          minimum: 0
          title: Credits Per Unit
          type: integer
        multiplier:
          description: 2 on the parse part of a parse_mode "high" job, otherwise 1.
          minimum: 1
          title: Multiplier
          type: integer
        part:
          description: >-
            parse: reading the document into text. operation: the tool's own
            work on what was parsed.
          enum:
            - parse
            - operation
          title: Part
          type: string
        quantity:
          description: How many units this part was charged for.
          minimum: 0
          title: Quantity
          type: integer
        unit:
          description: >-
            What quantity counts: page is a page or page-equivalent (a
            spreadsheet's non-empty cells, a minute of audio), item is a ground
            item, unit is a job priced by its units directly (agent steps).
          enum:
            - page
            - item
            - unit
          title: Unit
          type: string
        waiver:
          anyOf:
            - $ref: '#/components/schemas/UsageWaiver'
            - type: 'null'
          description: Which work was not charged and why. Null when charged.
      required:
        - part
        - quantity
        - unit
        - credits_per_unit
        - multiplier
        - credits
      title: UsageLine
      type: object
    AutomaticLlmRouter:
      additionalProperties: false
      description: An LLM chose the route. Its explanation is the routing's ``reason``.
      properties:
        kind:
          const: llm
          default: llm
          title: Kind
          type: string
      title: AutomaticLlmRouter
      type: object
    AutomaticNaceDmRouter:
      additionalProperties: false
      description: >-
        NaceDM, the decision model, chose the route by scoring how likely the
        query needs the agent.
      properties:
        decision_point:
          description: >-
            The agent runs when ``p_agent`` is at or above this; fact search
            runs below it.
          maximum: 1
          minimum: 0
          title: Decision Point
          type: number
        kind:
          const: nacedm
          default: nacedm
          title: Kind
          type: string
        p_agent:
          anyOf:
            - maximum: 1
              minimum: 0
              type: number
            - type: 'null'
          description: >-
            NaceDM's probability that the query needs the investigating agent
            rather than one fact-search pass; 1 - p_agent is the probability
            that fact search settles it. Null when the request was too long for
            NaceDM to score, which routes to the agent.
          title: P Agent
      required:
        - p_agent
        - decision_point
      title: AutomaticNaceDmRouter
      type: object
    AutomaticRequestedRouter:
      additionalProperties: false
      description: 'No router ran: the request''s ``mode`` named the route.'
      properties:
        kind:
          const: requested
          default: requested
          title: Kind
          type: string
      title: AutomaticRequestedRouter
      type: object
    TenantPurgeStore:
      description: >-
        One place tenant data lives, and so one line of a purge's verification
        report.


        A purge never deletes a Postgres row: each store's rows are emptied or
        de-identified and marked deleted, and

        only stored objects and Redis keys are removed.
      enum:
        - jobs
        - workspaces
        - files
        - upload_sessions
        - profile_cache
        - answer_cache
        - catalog
        - ledger_decisions
        - parse_exports
        - webhooks
        - api_keys
        - members
        - users
        - cli_sessions
        - limit_overrides
        - schemas_and_labels
        - perception_cache
        - storage
        - redis
      title: TenantPurgeStore
      type: string
    UsageWaiver:
      additionalProperties: false
      description: Why part of a job, or the whole job, was not charged.
      properties:
        part:
          description: >-
            parse: the parse part was not charged. job: nothing in the job was
            charged.
          enum:
            - parse
            - job
          title: Part
          type: string
        reason:
          description: >-
            reused_parse: the job read a parse your earlier job already paid
            for. repeat_of_job: the job repeated your earlier job exactly and
            was answered with its result.
          enum:
            - reused_parse
            - repeat_of_job
          title: Reason
          type: string
        source_job_id:
          anyOf:
            - format: uuid
              type: string
            - type: 'null'
          description: >-
            Always null: billing history must not disclose another job as its
            visibility changes.
          title: Source Job Id
      required:
        - part
        - reason
      title: UsageWaiver
      type: object
  securitySchemes:
    APIKeyHeader:
      in: header
      name: X-API-Key
      type: apiKey

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.