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

# Start a detection

> Starts analysis for a document and returns 202 with the detection in the `pending` state; the analysis runs in the background. Watch the detection's status via the SSE stream at `.../detections/{detectionId}/events` (or re-read the detection) and fetch the findings from `.../detections/{detectionId}/analysis/` once it reaches `complete`. A repeated Idempotency-Key returns the existing detection.



## OpenAPI

````yaml /api-reference/openapi.json post /workspaces/{workspaceId}/pipelines/{pipelineId}/detections
openapi: 3.1.0
info:
  title: Nvisy API
  summary: Document detection and redaction platform
  description: >-
    Nvisy provides intelligent detection of sensitive content in documents,
    redaction pipelines, and reviewer workflows. This API enables document
    upload, detection analysis, redaction, and collaborative review across your
    workspaces.
  termsOfService: https://nvisy.com/legal/terms-of-service
  contact:
    name: Nvisy Support
    url: https://nvisy.com
    email: hello@nvisy.com
  license:
    name: Apache-2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  version: 0.1.0
servers: []
security: []
tags:
  - name: Health
    description: Service health checks
  - name: Capabilities
    description: Read-only reference data describing this deployment
  - name: Accounts
    description: Account management and profile operations
  - name: Identities
    description: 'Account sign-in methods: password and linked providers'
  - name: Avatars
    description: Public avatar image serving
  - name: Authentication
    description: Login, signup, and token management
  - name: Workspaces
    description: Workspace creation and management
  - name: Activities
    description: Workspace activity log and export
  - name: Documents
    description: Document upload, download, and management
  - name: Analytics
    description: Aggregate metrics over a workspace's documents
  - name: Members
    description: Workspace member management
  - name: Invites
    description: Workspace invitation handling
  - name: API Tokens
    description: API token management
  - name: Connections
    description: External provider connections
  - name: Connection Syncs
    description: Connection synchronization runs
  - name: Providers
    description: Inference-provider configuration
  - name: Pipelines
    description: Redaction pipeline configuration
  - name: Detections
    description: Detection analysis and redaction
  - name: Redactions
    description: Redactions produced from a detection, with reviewer edits
  - name: Reviews
    description: Document reviews, the review queue, and their discussion
  - name: Policies
    description: Redaction policy configuration
  - name: Webhooks
    description: Webhook configuration
  - name: Notifications
    description: Account notification management
paths:
  /workspaces/{workspaceId}/pipelines/{pipelineId}/detections:
    post:
      tags:
        - Detections
      summary: Start a detection
      description: >-
        Starts analysis for a document and returns 202 with the detection in the
        `pending` state; the analysis runs in the background. Watch the
        detection's status via the SSE stream at
        `.../detections/{detectionId}/events` (or re-read the detection) and
        fetch the findings from `.../detections/{detectionId}/analysis/` once it
        reaches `complete`. A repeated Idempotency-Key returns the existing
        detection.
      parameters:
        - in: path
          name: workspaceId
          description: Workspace identifier.
          required: true
          schema:
            description: Workspace identifier.
            type: string
            format: uuid
          style: simple
        - in: path
          name: pipelineId
          description: Id of the pipeline.
          required: true
          schema:
            description: Id of the pipeline.
            type: string
            format: uuid
          style: simple
      requestBody:
        description: >-
          Request payload to start a detection over a document.


          Analyzes the document with the pipeline's configuration and returns
          the

          detection, which holds the findings for review before redaction.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkspaceDetection'
        required: true
      responses:
        '200':
          description: >-
            Response type for a detection.


            A detection is addressed by its own opaque id; the owning pipeline
            and

            workspace handles are carried for context. Redacted outputs are not
            here — a

            detection produces many redactions, each fetched from its
            `redactions`

            endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceDetection'
        '202':
          description: >-
            Response type for a detection.


            A detection is addressed by its own opaque id; the owning pipeline
            and

            workspace handles are carried for context. Redacted outputs are not
            here — a

            detection produces many redactions, each fetched from its
            `redactions`

            endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceDetection'
        '400':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '415':
          description: 'Expected request with `Content-Type: application/json`'
          content:
            text/plain:
              schema:
                type: string
        '422':
          description: Failed to deserialize the JSON body into the target type
          content:
            text/plain:
              schema:
                type: string
      security:
        - BearerAuth: []
components:
  schemas:
    CreateWorkspaceDetection:
      description: |-
        Request payload to start a detection over a document.

        Analyzes the document with the pipeline's configuration and returns the
        detection, which holds the findings for review before redaction.
      type: object
      properties:
        documentId:
          description: The document to analyze.
          type: string
          format: uuid
        scope:
          $ref: '#/components/schemas/DocumentContext'
          description: >-
            Per-document scope (languages, jurisdictions, document labels).


            Overrides the pipeline's `defaultScope` when present; absent falls
            back to

            the pipeline default.
      required:
        - documentId
    WorkspaceDetection:
      description: >-
        Response type for a detection.


        A detection is addressed by its own opaque id; the owning pipeline and

        workspace handles are carried for context. Redacted outputs are not here
        — a

        detection produces many redactions, each fetched from its `redactions`

        endpoint.
      type: object
      properties:
        completedAt:
          description: When the detection completed analysis.
          type: string
          format: date-time
        error:
          description: >-
            Human-readable failure reason, present only when the detection
            `failed`.
          type: string
        id:
          description: Opaque identifier of the detection.
          allOf:
            - $ref: '#/components/schemas/DetectionId'
        inputDocumentId:
          description: Source document this detection analyzes.
          type: string
          format: uuid
        inputDocumentName:
          description: >-
            Display name of the source document, for showing the detection
            without a

            separate file lookup. `None` if the file was removed (e.g. by
            retention).
          type: string
        metadata:
          description: Non-encrypted metadata for filtering/display.
          allOf:
            - $ref: '#/components/schemas/DetectionMetadata'
        pipelineId:
          description: >-
            Unique identifier of the pipeline this detection belongs to; absent
            for an

            ad-hoc detection.
          type: string
          format: uuid
        startedAt:
          description: When the detection started.
          type: string
          format: date-time
        status:
          description: >-
            Current detection status.


            The detections are available to fetch from the detection's
            `analysis`

            endpoint once this reaches `complete`.
          allOf:
            - $ref: '#/components/schemas/DetectionStatus'
        triggerType:
          description: How the detection was triggered.
          allOf:
            - $ref: '#/components/schemas/PipelineTriggerType'
        triggeredBy:
          description: Account that triggered the detection.
          allOf:
            - $ref: '#/components/schemas/AccountRef'
        workspaceHandle:
          description: URL-safe workspace handle. Display-only.
          allOf:
            - $ref: '#/components/schemas/Handle'
        workspaceId:
          description: Unique identifier of the workspace.
          type: string
          format: uuid
      required:
        - id
        - workspaceId
        - workspaceHandle
        - inputDocumentId
        - triggeredBy
        - triggerType
        - status
        - metadata
        - startedAt
    ErrorResponse:
      description: >-
        The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
        view

        that [`Error`] renders to at the response boundary.


        It carries no builder logic — [`Error`] is the type handlers construct
        and

        thread through `Result`, and it builds an `ErrorResponse` directly in
        its

        `IntoResponse` impl. `context` and `status` are not part of the JSON
        body

        (`context` is logged, `status` sets the HTTP status line).


        [`Error`]: crate::response::Error
      type: object
      properties:
        message:
          description: User-friendly error message safe for client display.
          type: string
        name:
          description: The error name/type identifier.
          type: string
      required:
        - name
        - message
    DocumentContext:
      description: |-
        What a caller asserts about the document being processed.

        Everything defaults to empty, so a caller asserting nothing
        passes [`DocumentContext::default`] and lets the recognizers use
        their own defaults.
      type: object
      properties:
        countries:
          description: |-
            Jurisdictions the caller asserts apply.

            Read by policy predicates that vary by jurisdiction, so a
            rule can act on a document from one country and not another.
          type: array
          items:
            $ref: '#/components/schemas/CountryCode'
        languages:
          description: |-
            Languages the caller asserts the document is in.

            Recognizers that take a language hint use it; the
            language-detection enricher fills the gap when this is
            empty.
          allOf:
            - $ref: '#/components/schemas/Languages'
          default: []
        metadata:
          description: |-
            Free-form request context: document tags, request purpose,
            output audience. See elide's [`ScopeMetadata`].
          allOf:
            - $ref: '#/components/schemas/ScopeMetadata'
    DetectionId:
      description: Opaque detection identifier (detection_<uuid>).
      type: string
      pattern: >-
        ^detection_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$
    DetectionMetadata:
      description: Structured metadata for a detection.
      type: object
      properties:
        error:
          description: Failure reason recorded when the detection failed.
          type: string
        tags:
          description: Free-form labels attached to the detection.
          type: array
          items:
            type: string
        usage:
          description: >-
            The engine's full per-recognizer usage report (durations, per-model
            token

            counts), stored opaquely for drill-down. Per-model token totals for

            aggregation live in the `workspace_detection_usage` table; this
            keeps the

            detail. Absent when the detection produced no usage.
    DetectionStatus:
      description: >-
        The execution status of a detection (one analysis pass of a file).


        Corresponds to the `DETECTION_STATUS` `PostgreSQL` enum. A detection is

        `Pending` (enqueued, no worker yet), then `Executing` (a worker is
        actively

        analyzing), then settles into `Complete` (analysis done, ready to
        redact)

        or `Failed`. Redaction is a separate, repeatable action over a complete

        detection and does not change this status.
      oneOf:
        - description: Enqueued for detection; no worker has picked it up yet.
          type: string
          const: pending
        - description: A worker is actively analyzing the document.
          type: string
          const: executing
        - description: Analysis done; the detection is ready to redact.
          type: string
          const: complete
        - description: Detection failed with an error.
          type: string
          const: failed
    PipelineTriggerType:
      description: >-
        How a pipeline run was initiated.


        Corresponds to the `PIPELINE_TRIGGER_TYPE` `PostgreSQL` enum: a run is
        either

        started directly by a user or automatically by the system (for example,
        a

        file upload that the pipeline auto-redacts).
      oneOf:
        - description: Started directly by a user.
          type: string
          const: user
        - description: >-
            Started automatically by the system (e.g. a file upload
            auto-redacted

            by the pipeline).
          type: string
          const: system
    AccountRef:
      description: >-
        Public reference to the account behind a resource — whoever created it,

        uploaded it, triggered it, or performed it.


        Reused across resource responses so an account is always presented the
        same

        way: its immutable id (the durable reference) plus a handle and optional

        avatar for display.
      type: object
      properties:
        avatarUrl:
          description: Serve path of the account's avatar, when set.
          type: string
        displayName:
          description: Human-readable display name, when set.
          type: string
        id:
          description: Immutable id of the account.
          type: string
          format: uuid
        username:
          description: Handle of the account (display).
          allOf:
            - $ref: '#/components/schemas/Handle'
      required:
        - id
        - username
    Handle:
      description: >-
        Lowercase, dash-separated identifier used in URLs and as account
        handles.
      type: string
      maxLength: 32
      minLength: 3
      pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
    CountryCode:
      description: |-
        [ISO 3166-1] country, identified by its code.

        Wraps [`celes::Country`], a static table entry carrying the numeric,
        alpha-2, and alpha-3 codes together with the country's name. Because
        every value comes from that fixed table, a `CountryCode` is always a
        real, recognised country; there is no way to hold an invalid one.

        Used to scope region-sensitive recognizers (a phone-number or
        national-id pattern, say) to the country whose format they target.

        Serializes as its alpha-2 code (e.g. `"US"`).

        [ISO 3166-1]: https://www.iso.org/iso-3166-country-codes.html
      type: string
    Languages:
      description: |-
        List of [`Language`]s resolved for one text scan.

        Built by a detector (one entry per detected region) or by the caller
        asserting languages. Carried on a [`RecognizerContext`] so every
        recognizer and the context enhancer can consult the call's languages.

        [`RecognizerContext`]: crate::recognition::RecognizerContext
      type: array
      items:
        $ref: '#/components/schemas/Language'
    ScopeMetadata:
      description: >-
        Caller-asserted scope shared across every payload of one analysis.


        Built once with the `with_*` chain and passed by reference to the

        analyzer, which borrows it into a fresh [`RecognizerContext`] per

        payload. It holds only what the *caller* asserts about the analysis as a

        whole, languages, jurisdictions, document labels, the target catalog, a

        correlation id, none of which depends on the medium, so one [`Scope`]

        drives a text, image, or audio analysis alike.


        Per-medium regions (caller-supplied inclusions and exclusions, which are

        `M::Location`-typed) live in [`Annotations`], attached to the analyzer

        of that modality. The per-payload working state (NLP artifacts, detected

        languages) lives on the context, not here.


        [`RecognizerContext`]: super::RecognizerContext

        [`Annotations`]: super::annotation::Annotations

        Free-form, caller-asserted request context: the *document* it is about
        and

        the *request* driving it.


        Three axes of opaque classification strings elide neither ships nor

        interprets, a downstream policy layer chooses what `"medical"` or

        `"fraud_detection"` or `"auditor"` mean. They are read in two places: a

        recognizer may bias its detection on them (the LLM prompt lists them so
        the

        model attends to the right terms), and a scope-aware operator predicate
        may

        branch on them at selection time (redact the same document differently
        per

        [`audience`]).


        - [`tags`] classify the *document* (`"medical"`, `"gdpr-request"`).

        - [`purpose`] is why the request exists (`"fraud_detection"`).

        - [`audience`] is who the redacted output is for (`"support_agent"`,
          `"auditor"`), the axis PCI-style "same document, two masks" branches on.

        [`tags`]: Self::tags

        [`purpose`]: Self::purpose

        [`audience`]: Self::audience
      type: object
      properties:
        audience:
          description: >-
            Who the redacted output is for (e.g. `"support_agent"`,
            `"auditor"`).

            The axis a per-audience redaction branches on: one detected
            document,

            selected differently per audience. May hold several.
          type: array
          default: []
          items:
            type: string
        purpose:
          description: >-
            The caller-asserted business purpose driving this request (e.g.

            `"fraud_detection"`, `"gdpr_erasure_request"`). A scope-aware
            operator

            predicate may skip or swap a rule based on it; a recognizer may bias

            detection on it. `None` when the caller asserts no purpose.
          type: string
        tags:
          description: |-
            Document-level classification tags (e.g. `"medical"`,
            `"gdpr-request"`). Recognizers may use these to bias their behavior
            for domain-specific terms; those that don't ignore the field.

            Named `tags`, not `labels`, to keep "label" reserved for the entity
            taxonomy ([`LabelRef`]/[`LabelCatalog`]): these classify the
            *document*, whereas the scope's catalog names the entity *types* to
            emit.

            [`LabelRef`]: crate::entity::LabelRef
            [`LabelCatalog`]: crate::entity::LabelCatalog
          type: array
          default: []
          items:
            type: string
    Language:
      description: |-
        Single language detection result.

        Carries the language plus an optional confidence and an optional
        byte-offset [`LanguageSpan`]. Backends that don't expose confidence
        leave it `None`; single-language detectors that don't track per-region
        information leave `span` as `None`. The `provenance` field records
        whether the answer came from a detector or was asserted by the caller.
      type: object
      properties:
        confidence:
          $ref: '#/components/schemas/Confidence'
          description: Optional confidence score. `None` when not exposed.
        language:
          description: Language.
          allOf:
            - $ref: '#/components/schemas/LanguageTag'
        provenance:
          description: 'How this language was obtained: detected or caller-asserted.'
          allOf:
            - $ref: '#/components/schemas/LanguageProvenance'
        span:
          $ref: '#/components/schemas/LanguageSpan'
          description: |-
            Byte-offset range this detection applies to, when known. `None`
            means the whole text.
      required:
        - language
        - provenance
    Confidence:
      description: |-
        Confidence score in the closed range `0.0..=1.0`.

        Carried by every provenance [`AuditEvent`] (the `before`/`after` of a
        recognition, fusion, or calibration) and by the effective confidence
        of an [`Entity`]. The newtype enforces the range at construction so
        no downstream code has to defend against values outside `[0, 1]`.

        Distinct from [`ConfidenceThreshold`] so the two cannot be confused
        at a call site: a score is *produced* by detection, a threshold is a
        *cutoff* configured to filter scores. Compare the two with
        [`ConfidenceThreshold::passes`].

        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`Entity`]: crate::entity::Entity
      type: number
      format: float
    LanguageTag:
      description: |-
        Well-formed [BCP 47] language tag, such as `en`, `en-US`, or
        `zh-Hant-HK`.

        Wraps [`oxilangtag::LanguageTag`] over a [`HipStr`] backing store, so
        short tags (the overwhelming common case) stay inline without a heap
        allocation. Parsing validates the tag's structure up front; the
        newtype therefore guarantees that any `LanguageTag` value in the
        model is syntactically valid.

        Used to record the language a recognizer is scoped to, or the
        detected language of a span of content.

        [BCP 47]: https://www.rfc-editor.org/info/bcp47
      type: string
    LanguageProvenance:
      description: |-
        How a [`Language`]'s language was obtained.

        Lets consumers distinguish "a detector ran and got this answer" from
        "the caller asserted this language". An assertion may still carry an
        optional confidence, so this is independent of the confidence field.
      oneOf:
        - description: Produced by a language-detection backend.
          type: string
          const: detected
        - description: Asserted by the caller.
          type: string
          const: asserted
    LanguageSpan:
      description: |-
        Byte-offset range within the analyzed text.

        Attached to a [`Language`] when the detector knows the span
        its answer covers (mixed-language input produces multiple detections,
        each with a distinct span). Single-language detections from
        non-segmenting backends, and caller-asserted answers, typically leave
        the span as `None`.
      type: object
      properties:
        end:
          description: Byte offset of the span end in the original text.
          type: integer
          format: uint
          minimum: 0
        start:
          description: Byte offset of the span start in the original text.
          type: integer
          format: uint
          minimum: 0
      required:
        - start
        - end

````