> ## 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.

# Get policy

> Returns a single policy.



## OpenAPI

````yaml /api-reference/openapi.json get /workspaces/{workspaceSlug}/policies/{policySlug}/
openapi: 3.1.0
info:
  title: Nvisy API
  summary: Document processing and annotation platform
  description: >-
    Nvisy provides intelligent document processing, annotation, and analysis
    capabilities. This API enables document upload, OCR processing, embedding
    generation, and semantic search across your document collections.
  termsOfService: https://nvisy.com/legal/terms-of-service
  contact:
    name: Nvisy Support
    url: https://nvisy.com
    email: hello@nvisy.com
  license:
    name: Proprietary
    url: https://nvisy.com/license
  version: 0.1.0
servers: []
security: []
tags:
  - name: Accounts
    description: Account management and profile operations
  - name: Authentication
    description: Login, signup, and token management
  - name: Workspaces
    description: Workspace creation and management
  - name: Files
    description: File upload, download, and management
  - 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: Pipelines
    description: Redaction pipeline configuration
  - name: Pipeline Runs
    description: Pipeline run execution and review
  - name: Policies
    description: Redaction policy configuration
  - name: Webhooks
    description: Webhook configuration
  - name: Notifications
    description: Account notification management
  - name: Health
    description: Service health checks
paths:
  /workspaces/{workspaceSlug}/policies/{policySlug}/:
    get:
      tags:
        - Policies
      summary: Get policy
      description: Returns a single policy.
      parameters:
        - in: path
          name: workspaceSlug
          description: URL-safe workspace identifier.
          required: true
          schema:
            description: URL-safe workspace identifier.
            type: string
          style: simple
        - in: path
          name: policySlug
          description: URL slug of the policy, unique within its workspace.
          required: true
          schema:
            description: URL slug of the policy, unique within its workspace.
            type: string
          style: simple
      responses:
        '200':
          description: Response type for a workspace policy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Policy'
        '401':
          description: >-
            HTTP error response representation with security-conscious design.


            This struct contains all the information needed to serialize an
            error

            response, including the error name, message, HTTP status code,
            resource

            information, and user-friendly messages.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            HTTP error response representation with security-conscious design.


            This struct contains all the information needed to serialize an
            error

            response, including the error name, message, HTTP status code,
            resource

            information, and user-friendly messages.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            HTTP error response representation with security-conscious design.


            This struct contains all the information needed to serialize an
            error

            response, including the error name, message, HTTP status code,
            resource

            information, and user-friendly messages.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - BearerAuth: []
components:
  schemas:
    Policy:
      description: Response type for a workspace policy.
      type: object
      properties:
        createdAt:
          description: When the policy was created.
          type: string
          format: date-time
        createdBy:
          description: Account that created this policy.
          allOf:
            - $ref: '#/components/schemas/AccountRef'
        definition:
          description: The structured policy body consumed by the engine.
          allOf:
            - $ref: '#/components/schemas/PolicyDefinition'
        description:
          description: Policy description.
          type: string
        displayName:
          description: Human-readable policy display name.
          type: string
        slug:
          description: URL slug of the policy, unique within its workspace.
          allOf:
            - $ref: '#/components/schemas/Handle'
        updatedAt:
          description: When the policy was last updated.
          type: string
          format: date-time
        workspaceSlug:
          description: Handle of the workspace this policy belongs to.
          allOf:
            - $ref: '#/components/schemas/Handle'
      required:
        - slug
        - workspaceSlug
        - createdBy
        - displayName
        - definition
        - createdAt
        - updatedAt
    ErrorResponse:
      description: |-
        HTTP error response representation with security-conscious design.

        This struct contains all the information needed to serialize an error
        response, including the error name, message, HTTP status code, resource
        information, and user-friendly messages.
      type: object
      properties:
        message:
          description: User-friendly error message safe for client display
          type: string
        name:
          description: The error name/type identifier
          type: string
        resource:
          description: The resource that the error relates to (optional, set by handler)
          type: string
        suggestion:
          description: Helpful suggestion for resolving the error (optional)
          type: string
        validation:
          description: Validation error details for field-specific errors
          type: array
          items:
            $ref: '#/components/schemas/ValidationErrorDetail'
      required:
        - name
        - message
    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: a handle plus an optional avatar.
      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
        username:
          description: Handle of the account.
          allOf:
            - $ref: '#/components/schemas/Handle'
      required:
        - username
    PolicyDefinition:
      description: |-
        A named governance policy.

        Identity is the UUID; `name` is display-only.
      type: object
      properties:
        description:
          description: Optional description for reviewers.
          type: string
        fallback:
          $ref: '#/components/schemas/ModalityRedactions'
          description: |-
            Per-policy catch-all. Fires when no rule in this policy
            matched. Presence halts the chain; absence falls through
            to the next policy. [`Option`] enforces "at most one
            fallback per policy" at the type level.
        groups:
          description: |-
            Named clusters of [`LabelRef`]s this policy's rules may
            reference by name via [`Predicate::LabelInGroup`]. Scoped
            to this policy — a rule can only name a group its own
            policy declared; unknown references error at request
            validation. Two policies that both declare `hipaa_18` with
            different labelsets stay independent.

            [`LabelRef`]: elide_core::entity::LabelRef
            [`Predicate::LabelInGroup`]: predicate::Predicate::LabelInGroup
          type: array
          items:
            $ref: '#/components/schemas/LabelGroup'
        id:
          description: |-
            Stable identifier. UUIDv7 recommended (time-ordered);
            customer-supplied so re-submissions carry the same id.
            Engine stamps this into the redaction event's
            [`Attribution::name`] so reviewers can find this policy
            from any redaction it drove.

            [`Attribution::name`]: elide_core::entity::audit::Attribution::name
          type: string
          format: uuid
        labels:
          description: |-
            Vocabulary the policy operates over: builtins picked by
            name plus caller-authored custom label schemas. Engine
            unions every submitted policy's `labels` into a per-request
            [`LabelCatalog`] used to drive recognizer dispatch and
            tag-based [`Predicate::TagOneOf`] matching.

            [`LabelCatalog`]: elide_core::entity::LabelCatalog
            [`Predicate::TagOneOf`]: predicate::Predicate::TagOneOf
          allOf:
            - $ref: '#/components/schemas/Labels'
        name:
          description: Human-readable name. Display-only. Does not key anything.
          type: string
        rules:
          description: Ordered rules. First match wins within this policy.
          type: array
          items:
            $ref: '#/components/schemas/PolicyRule'
      required:
        - id
        - name
    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]+)*$
    ValidationErrorDetail:
      description: Validation error details for field-specific errors.
      type: object
      properties:
        code:
          description: Error code for the validation failure
          type: string
        field:
          description: Field name that failed validation
          type: string
        message:
          description: Human-readable error message
          type: string
        params:
          description: Additional parameters related to the validation error
          type: object
          additionalProperties: true
      required:
        - field
        - code
        - message
    ModalityRedactions:
      description: |-
        Per-modality operator specs carried by a `redact` rule.

        A single rule can name an operator for every modality the
        workspace supports. At apply time the redaction phase picks the
        operator matching the entity's modality (engine reads the right
        field directly via `redactions.text.as_ref()` etc.); modalities
        the rule didn't cover fall through to the deployment-wide
        default, and entities with no operator from either source are
        skipped.
      type: object
      properties:
        audio:
          $ref: '#/components/schemas/AudioRedaction'
          description: Operator for audio-modality entities.
        image:
          $ref: '#/components/schemas/ImageRedaction'
          description: Operator for image-modality entities.
        tabular:
          $ref: '#/components/schemas/TabularRedaction'
          description: Operator for tabular-modality entities.
        text:
          $ref: '#/components/schemas/TextRedaction'
          description: Operator for text-modality entities.
    LabelGroup:
      description: >-
        Named cluster of [`LabelRef`]s a policy's rules can reference

        by name via [`Predicate::LabelInGroup`].


        Groups live on the [`PolicyDefinition`] that declares them

        and are visible only to that policy's own rules. Templates

        ship one group per canonical label list (`"hipaa_18"`,

        `"gdpr_article_9"`, `"pci_chd"`, `"pci_sad"`), and every

        rule that targets that list references the group by name

        instead of respelling the labels. When elide adds a new label

        to a category, extending the group covers every rule that

        referenced it — no rule edit.


        **Compilation**: at request time the engine synthesises a

        `group:<policy_id>:<name>` tag on every label listed in the

        group, then rewrites [`Predicate::LabelInGroup { group }`]

        into [`Predicate::TagOneOf { tags: ["group:<policy_id>:<name>"] }`].

        That routes through the same `Anonymizer::with_tag` fast

        path as any authored tag — no new engine machinery, no

        per-request walk over group membership. Scoping the tag by

        `policy_id` keeps two policies that both declare `hipaa_18`

        with different labelsets from stepping on each other.


        **Unknown group names error at request validation**, not at

        apply time. A typo doesn't silently underfire.


        [`PolicyDefinition`]: super::PolicyDefinition

        [`Predicate::LabelInGroup`]: super::predicate::Predicate::LabelInGroup

        [`Predicate::LabelInGroup { group }`]:
        super::predicate::Predicate::LabelInGroup

        [`Predicate::TagOneOf { tags: ["group:<policy_id>:<name>"] }`]:
        super::predicate::Predicate::TagOneOf
      type: object
      properties:
        description:
          description: Optional description for reviewers.
          type: string
        labels:
          description: |-
            Labels this group covers, by ref.

            A label that doesn't appear in the request's compiled
            [`LabelCatalog`] is silently skipped at tag-synthesis time
            — a group can safely list labels the current build
            doesn't emit (e.g. modality-gated ones); rules keyed off
            the group still fire on whatever labels *are* present.

            [`LabelCatalog`]: elide_core::entity::LabelCatalog
          type: array
          items:
            $ref: '#/components/schemas/LabelRef'
        name:
          description: >-
            Stable name a [`Predicate::LabelInGroup`] references.


            Free-form; a policy layer picks the vocabulary. Recommend

            snake_case identifiers (`hipaa_18`, `gdpr_article_9`) —

            they compile to `group:hipaa_18` tags on the catalog and

            read cleanly in audit provenance.


            [`Predicate::LabelInGroup`]:
            super::predicate::Predicate::LabelInGroup
          type: string
      required:
        - name
        - labels
    Labels:
      description: |-
        Per-policy label-catalog selection.

        Picks builtins by name + adds inline custom schemas.
      type: object
      properties:
        builtins:
          description: |-
            Builtin label names to enable.

            E.g. `"email_address"`, `"phone_number"`. Unknown names
            log a warning and are skipped.
          type: array
          items:
            $ref: '#/components/schemas/LabelRef'
        custom:
          description: Custom labels defined inline by the caller.
          type: array
          items:
            $ref: '#/components/schemas/Label'
    PolicyRule:
      description: |-
        One rule inside a [`PolicyDefinition`]. Identity is the UUID;
        `name` / `description` are display-only. `dispatch` picks the
        selection strategy.

        [`PolicyDefinition`]: super::PolicyDefinition
      type: object
      properties:
        description:
          description: Optional description for reviewers.
          type: string
        id:
          description: >-
            Stable identifier. UUIDv7 recommended. Engine stamps it

            into the redaction event's [`Attribution::description`] so

            reviewers can trace which rule fired. Every attachment a

            [`RuleDispatch::Table`] expands into shares this UUID.


            [`Attribution::description`]:
            elide_core::entity::audit::Attribution::description
          type: string
          format: uuid
        name:
          description: Human-readable name. Display-only.
          type: string
      oneOf:
        - description: |-
            One predicate, one action. Fires when the predicate holds
            on the candidate entity.
          type: object
          properties:
            action:
              description: |-
                Per-modality redaction operators applied when the
                predicate matches. Modalities the rule doesn't cover
                fall through to the policy fallback (or the next
                policy in the chain).

                Boxed to keep [`RuleDispatch`]'s stack footprint
                small — [`ModalityRedactions`] carries four optional
                per-modality operator enums and dominates the variant
                size. `Table`'s `Vec<LabelEntry>` already heap-allocates
                its entries, so boxing here keeps the two variants
                balanced without changing the wire form.
              allOf:
                - $ref: '#/components/schemas/ModalityRedactions'
            kind:
              type: string
              const: predicated
            predicate:
              description: |-
                Entity-level predicate that decides whether the rule
                fires on a given recognised entity. Composable; see
                [`Predicate`] for the full grammar.
              allOf:
                - $ref: '#/components/schemas/Predicate'
          required:
            - kind
            - predicate
            - action
        - description: |-
            N `(label, action)` entries under one shared identity.
            Every entity whose label matches a listed [`LabelRef`]
            attaches the paired [`ModalityRedactions`]. Labels absent
            from the list are not affected by this rule and fall
            through to the next rule or the policy fallback.

            A [`Vec`] rather than a map keeps the author-supplied
            order — elide's anonymizer is first-match-wins, so wire
            order determines which entry fires when two match the
            same entity. Duplicate labels are the caller's bug; the
            engine attaches every entry, and the first one wins.
          type: object
          properties:
            kind:
              type: string
              const: table
            operators:
              description: Per-label operator dispatch.
              type: array
              items:
                $ref: '#/components/schemas/LabelEntry'
          required:
            - kind
            - operators
      required:
        - id
        - name
    AudioRedaction:
      description: Operator spec a `redact` audio rule carries.
      oneOf:
        - description: Cut the matched interval out, shortening the clip.
          type: object
          properties:
            kind:
              type: string
              const: erase
          required:
            - kind
        - description: Pass the interval through unchanged.
          type: object
          properties:
            kind:
              type: string
              const: keep
          required:
            - kind
        - description: |-
            Zero the matched interval in place, preserving the clip
            length and the timing of everything after it.
          type: object
          properties:
            kind:
              type: string
              const: silence
          required:
            - kind
        - description: |-
            Overlay a tone over the matched interval (the broadcast
            "bleep"); preserves duration.
          type: object
          properties:
            amplitude:
              description: |-
                Peak amplitude in `0.0..=1.0` of full scale. Default 0.5:
                audible but not painful, and never clips.
              type: number
              format: float
              default: 0.5
            hz:
              description: |-
                Tone frequency in Hertz. Default 1 kHz, the broadcast
                convention.
              type: number
              format: float
              default: 1000
            kind:
              type: string
              const: beep
            waveform:
              description: Tone shape. Default sine.
              allOf:
                - $ref: '#/components/schemas/Waveform'
              default: sine
          required:
            - kind
    ImageRedaction:
      description: Operator spec a `redact` image rule carries.
      oneOf:
        - description: Clear the matched region.
          type: object
          properties:
            kind:
              type: string
              const: erase
          required:
            - kind
        - description: Pass the region through unchanged.
          type: object
          properties:
            kind:
              type: string
              const: keep
          required:
            - kind
        - description: Gaussian-blur the region.
          type: object
          properties:
            kind:
              type: string
              const: blur
            sigma:
              description: |-
                Standard deviation of the Gaussian kernel, in pixels.
                Larger is blurrier (and harder to reverse).
              type: number
              format: float
              default: 16
          required:
            - kind
        - description: Mosaic-pixelate the region.
          type: object
          properties:
            block_size:
              description: |-
                Side length of each mosaic block, in pixels. Larger
                blocks are coarser (and harder to reverse).
              type: integer
              format: uint32
              default: 16
              minimum: 0
            kind:
              type: string
              const: pixelate
          required:
            - kind
        - description: Cover the region with a solid color fill (black by default).
          type: object
          properties:
            color:
              description: Fill color the codec rasterises over the region.
              allOf:
                - $ref: '#/components/schemas/Color'
              default:
                b: 0
                g: 0
                r: 0
            kind:
              type: string
              const: blackbox
          required:
            - kind
    TabularRedaction:
      description: Operator spec a `redact` tabular rule carries.
      oneOf:
        - description: |-
            Run a text-modality operator on the matched cell's
            content. Cells are `TextBacked` in elide, so any
            [`TextRedaction`] applies unchanged.
          type: object
          properties:
            kind:
              type: string
              const: cell
            spec:
              description: |-
                The text operator to apply. Reuses the full text
                vocabulary (Erase, Keep, Mask, Replace, Hash,
                Pseudonymize, Encrypt).
              allOf:
                - $ref: '#/components/schemas/TextRedaction'
          required:
            - kind
            - spec
        - description: Drop the entire row a matched cell sits in.
          type: object
          properties:
            kind:
              type: string
              const: drop_row
          required:
            - kind
        - description: |-
            Drop the entire column a matched cell sits in (header
            included).
          type: object
          properties:
            kind:
              type: string
              const: drop_column
          required:
            - kind
    TextRedaction:
      description: Operator spec a `redact` text rule carries.
      oneOf:
        - description: Delete the matched span entirely.
          type: object
          properties:
            kind:
              type: string
              const: erase
          required:
            - kind
        - description: Pass the value through unchanged.
          type: object
          properties:
            kind:
              type: string
              const: keep
          required:
            - kind
        - description: |-
            Character-replacement masking. Leaves `keep_prefix` leading
            and `keep_suffix` trailing characters visible; masks the
            rest with `mask_char`.
          type: object
          properties:
            keep_prefix:
              description: |-
                Characters to leave unmasked at the start of the value.
                `0` (the default) masks from the start.
              type: integer
              format: uint
              minimum: 0
            keep_suffix:
              description: |-
                Characters to leave unmasked at the end of the value.
                `0` (the default) masks through to the end.
              type: integer
              format: uint
              minimum: 0
            kind:
              type: string
              const: mask
            mask_char:
              description: The character that replaces masked positions.
              type: string
              default: '*'
              maxLength: 1
              minLength: 1
          required:
            - kind
        - description: |-
            Substitute the span with a fixed template. Supports
            `{label}` / `{value}` / `{coref}` placeholders.
          type: object
          properties:
            kind:
              type: string
              const: replace
            template:
              description: Template string. Default `[{label}]`.
              type: string
              default: '[{label}]'
          required:
            - kind
        - description: One-way SHA-2 hash with optional salt.
          type: object
          properties:
            algorithm:
              description: SHA-256 (default) or SHA-512.
              allOf:
                - $ref: '#/components/schemas/Sha2Algorithm'
              default: sha256
            kind:
              type: string
              const: hash
            salt:
              description: Salt prepended to the value before hashing.
              type: string
          required:
            - kind
        - description: |-
            Swap the matched span for a locale-aware fake value. Picks
            the locale from the entity's BCP-47 `language` tag, falling
            back to `default_language` (English unless overridden) when
            the entity carries none. Coreferent mentions of the same
            real-world entity collapse to the same surrogate within a
            run. Labels outside the built-in PII catalogue fall through
            to [`Replace`] with `fallback_template`.

            [`Replace`]: TextRedaction::Replace
          type: object
          properties:
            default_language:
              $ref: '#/components/schemas/LanguageTag'
              description: |-
                BCP-47 tag used when the entity carries no language of
                its own. Defaults to English (`"en"`).
            fallback_template:
              description: |-
                Template used for entity labels outside the built-in
                PII catalogue (which `Fake` can't generate for).
                Supports the same `{label}` / `{value}` / `{coref}`
                placeholders as [`Replace`]. Defaults to `[{label}]`.

                [`Replace`]: TextRedaction::Replace
              type: string
              default: '[{label}]'
            kind:
              type: string
              const: fake
            seed:
              description: |-
                Seed mixed into per-entity RNG state. Two runs with the
                same seed and the same input entities produce the same
                surrogates. Defaults to `0`.
              type: integer
              format: uint64
              minimum: 0
          required:
            - kind
        - description: >-
            Vault-backed pseudonym: every mention of the same entity

            reads the same surrogate. The engine wires a per-request

            vault + the default [`RandomToken`] generator.


            [`RandomToken`]:
            https://docs.rs/elide/latest/elide/redaction/generator/struct.RandomToken.html
          type: object
          properties:
            kind:
              type: string
              const: pseudonymize
          required:
            - kind
        - description: |-
            Reversible AES-256-GCM ciphertext. The engine supplies the
            per-tenant AES key at construction so raw key material never
            lives in serialised policy. Requires the engine to have a
            key provider wired via `Engine::with_key_provider`.
          type: object
          properties:
            kind:
              type: string
              const: encrypt
          required:
            - kind
        - description: |-
            Keyed HMAC-SHA-2 digest. The key stays secret, so an
            attacker who obtains the redacted output cannot enumerate
            a small input space without the key — the PCI DSS v4.0.1
            §3.5.1 "keyed hash" posture. Distinct from [`Hash`], whose
            salt is public. Requires the engine to have a key provider
            wired via `Engine::with_key_provider`.

            [`Hash`]: TextRedaction::Hash
          type: object
          properties:
            algorithm:
              description: HMAC-SHA-256 (default) or HMAC-SHA-512.
              allOf:
                - $ref: '#/components/schemas/Sha2Algorithm'
              default: sha256
            kind:
              type: string
              const: hmac_hash
          required:
            - kind
        - description: |-
            Physically remove the middle of the value, keeping a
            leading and/or trailing run of characters. Unlike [`Mask`]
            this *shortens* the string — the dropped characters leave
            no placeholder. The PCI DSS §3.5.1 truncation posture for
            stored PAN: `Truncate { keep_prefix: 6, keep_suffix: 4 }`
            on `"4111111111111234"` yields `"4111111234"` (10 chars),
            where the analogous [`Mask`] would yield `"411111******1234"`
            (16 chars, length preserved).

            A configuration whose kept regions cover (or overlap) the
            whole value is rejected at apply time; the operator errors
            rather than silently pass through.

            [`Mask`]: TextRedaction::Mask
          type: object
          properties:
            keep_prefix:
              description: Characters to keep at the start of the value.
              type: integer
              format: uint
              minimum: 0
            keep_suffix:
              description: Characters to keep at the end of the value.
              type: integer
              format: uint
              minimum: 0
            kind:
              type: string
              const: truncate
          required:
            - kind
        - description: |-
            Collapse a numeric value at or above `ceiling` (or at or
            below `floor`) into a bucket label; values in the middle
            pass through unchanged. The HIPAA §164.514(b)(2)(i)(C) age
            posture: everyone ≥90 aggregates into `"90 or older"`,
            while a 73-year-old stays `"73"`.

            Values that don't parse as finite numbers are *declined*.
            `fallback` names what runs on a declined value; when
            absent, elide's baked-in default is [`Erase`] (the safe
            posture — a non-numeric age never survives redaction).

            [`Erase`]: TextRedaction::Erase
          type: object
          properties:
            ceiling:
              description: |-
                Threshold at or above which values collapse to
                `ceiling_bucket`. `None` disables the ceiling.
              type: number
              format: double
            ceiling_bucket:
              $ref: '#/components/schemas/ClampBucket'
              description: |-
                Bucket label for values at or above `ceiling`. Required
                when `ceiling` is set; ignored otherwise.
            fallback:
              $ref: '#/components/schemas/TerminalFallback'
              description: |-
                Operator that runs when the entity value isn't a
                finite number. `None` erases (elide's default).
            floor:
              description: |-
                Threshold at or below which values collapse to
                `floor_bucket`. `None` disables the floor.
              type: number
              format: double
            floor_bucket:
              $ref: '#/components/schemas/ClampBucket'
              description: |-
                Bucket label for values at or below `floor`. Required
                when `floor` is set; ignored otherwise.
            kind:
              type: string
              const: clamp
          required:
            - kind
        - description: |-
            Reduce a date/timestamp to a coarser granularity. The
            HIPAA §164.514(b)(2)(i)(C) date-generalization posture:
            dates directly related to an individual reduced to the
            year, preserving cohort-level analytics.

            Values that don't parse as dates are *declined*. `fallback`
            names what runs on a declined value; when absent, elide's
            baked-in default is [`Erase`].

            [`Erase`]: TextRedaction::Erase
          type: object
          properties:
            fallback:
              $ref: '#/components/schemas/TerminalFallback'
              description: |-
                Operator that runs when the entity value isn't a
                parseable date. `None` erases (elide's default).
            granularity:
              description: Coarseness of the output. Default `Year`.
              allOf:
                - $ref: '#/components/schemas/DateGranularity'
              default: year
            kind:
              type: string
              const: generalize_date
            style:
              description: Which input convention to accept. Default `Iso`.
              allOf:
                - $ref: '#/components/schemas/DateStyle'
              default: iso
          required:
            - kind
    LabelRef:
      description: |-
        Lightweight reference to a [`Label`], carrying only its id.

        This is what detections and entities hold: cloning is cheap (short
        ids inline into the [`HipStr`]), and the full [`Label`], with its
        localized names and descriptions, is resolved on demand from a
        [`LabelCatalog`].

        [`Label`]: crate::entity::Label
        [`LabelCatalog`]: crate::entity::LabelCatalog
      type: string
    Label:
      description: >-
        Kind of sensitive information: a stable [`id`], per-language

        [`LabelLocale`]s, an optional [`category`], and zero or more tags.


        # Identity


        Labels are identified by [`id`] — a stable lowercase `snake_case`

        string (`"phone_number"`), never localized, and the catalog key that a

        [`LabelRef`] resolves through. Selectors match by id. Derived equality

        is *structural*: two labels with the same id but different

        localizations or tags are not `==`; compare [`id`] for identity.


        # Localization


        The display name and description are localized per [`LanguageTag`].

        English (`"en"`) is required at construction and is the fallback when a

        requested locale is absent, so [`localization`] always returns some

        text — NER and LLM read the analysis language's name and description to

        prompt the model, keyed by the stable id.


        # Category and tags


        [`category`] is the single coarse group a label belongs to (`financial`,

        `health`, `identity`, …), for organizing detected entities by kind.
        Built-in

        labels ship with one; a custom label has none unless set.


        [`tags`] is a free-form list of *cross-cutting* markers a policy
        selector

        matches against — sensitivity flags a label may carry several of (`pii`,

        `phi`, `pci`, `sad`, `secret`). Distinct from the category: a label has
        at

        most one category but any number of tags. Custom labels can ship with
        zero

        of either.


        [`id`]: Label::id

        [`localization`]: Label::localization

        [`category`]: Label::category

        [`tags`]: Label::tags
      type: object
      properties:
        category:
          $ref: '#/components/schemas/Category'
        id:
          type: string
        localizations:
          $ref: '#/components/schemas/LocalizedText'
        tags:
          type: array
          items:
            type: string
      required:
        - id
        - localizations
        - tags
    Predicate:
      description: |-
        Predicate over a recognised entity. The wire format uses an
        internally tagged enum so authors write
        `{ "kind": "confidence", "min": 0.7 }` etc.
      oneOf:
        - description: Entity confidence is at least `min`.
          type: object
          properties:
            kind:
              type: string
              const: confidence
            min:
              description: Minimum confidence cutoff.
              allOf:
                - $ref: '#/components/schemas/ConfidenceThreshold'
          required:
            - kind
            - min
        - description: Entity label is one of `labels`.
          type: object
          properties:
            kind:
              type: string
              const: labelOneOf
            labels:
              description: Allowed labels.
              type: array
              items:
                $ref: '#/components/schemas/LabelRef'
          required:
            - kind
            - labels
        - description: |-
            Entity label carries one of `tags`, per the per-request
            label catalog.
          type: object
          properties:
            kind:
              type: string
              const: tagOneOf
            tags:
              description: Allowed tags.
              type: array
              items:
                type: string
          required:
            - kind
            - tags
        - description: |-
            Entity label is in the named [`LabelGroup`] declared by
            the same [`PolicyDefinition`] this rule lives in.

            Sugar over [`TagOneOf`]`{ tags: ["group:<policy_id>:<name>"] }`:
            the engine synthesises a `group:<policy_id>:<name>` tag on
            every label listed in the matching group at request-compile
            time, then rewrites `LabelInGroup { group }` to that
            `TagOneOf` form. Same fast path as any authored tag; the
            group indirection keeps the wire compact when templates
            target a canonical label cluster (e.g. `hipaa_18`,
            `gdpr_article_9`).

            Groups are scoped to the declaring policy — a rule cannot
            reference a group declared by another policy in the same
            request. An unknown group name is a request-validation
            error, not a silent no-op.

            [`PolicyDefinition`]: super::PolicyDefinition

            [`LabelGroup`]: super::LabelGroup
            [`TagOneOf`]: Predicate::TagOneOf
          type: object
          properties:
            group:
              description: |-
                Name of the [`LabelGroup`] to match against.

                [`LabelGroup`]: super::LabelGroup
              type: string
            kind:
              type: string
              const: labelInGroup
          required:
            - kind
            - group
        - description: Entity carries the given coreference cluster id.
          type: object
          properties:
            coref:
              description: Cluster id to match.
              type: string
            kind:
              type: string
              const: coRef
          required:
            - kind
            - coref
        - description: All sub-predicates must hold (AND).
          type: object
          properties:
            all:
              description: Conjunction members.
              type: array
              items:
                $ref: '#/components/schemas/Predicate'
            kind:
              type: string
              const: all
          required:
            - kind
            - all
        - description: At least one sub-predicate must hold (OR).
          type: object
          properties:
            any:
              description: Disjunction members.
              type: array
              items:
                $ref: '#/components/schemas/Predicate'
            kind:
              type: string
              const: any
          required:
            - kind
            - any
        - description: Sub-predicate must not hold (NOT).
          type: object
          properties:
            kind:
              type: string
              const: not
            not:
              description: Negated predicate.
              allOf:
                - $ref: '#/components/schemas/Predicate'
          required:
            - kind
            - not
    LabelEntry:
      description: |-
        One entry inside a [`RuleDispatch::Table`]: the label to match
        plus the per-modality operators to run.

        Kept as a named struct rather than a `(LabelRef, ModalityRedactions)`
        tuple so the wire JSON reads `{"label": "email", "action": {…}}`
        instead of a positional pair.
      type: object
      properties:
        action:
          description: Per-modality operators to run for matching entities.
          allOf:
            - $ref: '#/components/schemas/ModalityRedactions'
        label:
          description: Label the entry matches on.
          allOf:
            - $ref: '#/components/schemas/LabelRef'
      required:
        - label
        - action
    Waveform:
      description: Shape of a synthesized tone.
      oneOf:
        - description: |-
            Pure sine. The broadcast censor-beep convention: audible but smooth,
            with no harmonics to alias on resampling.
          type: string
          const: sine
        - description: Square wave. Harsher and richer in harmonics — the "retro" bleep.
          type: string
          const: square
    Color:
      description: |-
        Color as 8-bit RGB.

        Used by visual redaction (a solid-fill block over an image region) and
        any other rendering instruction that needs a color.
      type: object
      properties:
        b:
          description: Blue channel.
          type: integer
          format: uint8
          maximum: 255
          minimum: 0
        g:
          description: Green channel.
          type: integer
          format: uint8
          maximum: 255
          minimum: 0
        r:
          description: Red channel.
          type: integer
          format: uint8
          maximum: 255
          minimum: 0
      required:
        - r
        - g
        - b
    Sha2Algorithm:
      description: |-
        Which SHA-2 variant a hashing operator uses.

        Shared by [`Sha2Hash`] (unkeyed digest) and [`HmacHash`] (keyed HMAC):
        both pick the same underlying width, so the choice lives in one enum
        rather than one per operator.

        [`Sha2Hash`]: super::Sha2Hash
        [`HmacHash`]: super::HmacHash
      oneOf:
        - description: SHA-256 — 32-byte digest, 64-char hex.
          type: string
          const: sha256
        - description: SHA-512 — 64-byte digest, 128-char hex.
          type: string
          const: sha512
    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
    ClampBucket:
      description: |-
        Text a [`TextRedaction::Clamp`] emits for out-of-range values.

        Three forms, deserialized untagged so callers can pick the
        terser one for the case at hand:

        - **Plain string** (`"90 or older"`) — English-only shorthand.
        - **Localized map** (`{"en": "90 or older", "fr": "90 ou plus"}`)
          — one entry per language the deployment ships; missing
          locales fall back to English at render time.
        - **Format template** (`{"format": "{n} or older"}`) — the
          engine substitutes `{n}` for the threshold, so a ceiling of
          `90` renders `"90 or older"` without the caller repeating the
          number. Same rendering in every language.

        Round-trips through serde as whichever form the caller wrote.
      anyOf:
        - description: Plain English-only string.
          type: string
        - description: |-
            Format template — the engine substitutes `{n}` for the
            threshold. Same rendering in every language.
          type: object
          properties:
            format:
              description: |-
                The template string. `{n}` is substituted; other text
                is literal.
              type: string
          required:
            - format
        - description: Localized map, keyed by BCP-47 language tag.
          type: object
          additionalProperties:
            type: string
    TerminalFallback:
      description: |-
        Fallback operator that runs when a declinable primary
        ([`TextRedaction::Clamp`], [`TextRedaction::GeneralizeDate`])
        doesn't apply to the entity value.

        The four operators that always apply and produce a deterministic
        output without needing engine-side infrastructure (no key
        provider, no vault). Enough to satisfy every regulatory
        pattern I know: `Clamp/GeneralizeDate → Erase` (the safe
        default), `→ Replace { template }` (an explicit placeholder),
        or `→ Mask` / `→ Keep` on the rare occasion those fit.

        Absent from the primary's spec, elide's baked-in default is
        [`Erase`] — a bare [`TextRedaction::Clamp`] without a `fallback`
        erases values that aren't numeric.

        [`Erase`]: TerminalFallback::Erase
      oneOf:
        - description: See [`TextRedaction::Erase`].
          type: object
          properties:
            kind:
              type: string
              const: erase
          required:
            - kind
        - description: See [`TextRedaction::Keep`].
          type: object
          properties:
            kind:
              type: string
              const: keep
          required:
            - kind
        - description: See [`TextRedaction::Replace`].
          type: object
          properties:
            kind:
              type: string
              const: replace
            template:
              description: Template string. Default `[{label}]`.
              type: string
              default: '[{label}]'
          required:
            - kind
        - description: See [`TextRedaction::Mask`].
          type: object
          properties:
            keep_prefix:
              description: Characters to leave unmasked at the start of the value.
              type: integer
              format: uint
              minimum: 0
            keep_suffix:
              description: Characters to leave unmasked at the end of the value.
              type: integer
              format: uint
              minimum: 0
            kind:
              type: string
              const: mask
            mask_char:
              description: The character that replaces masked positions.
              type: string
              default: '*'
              maxLength: 1
              minLength: 1
          required:
            - kind
    DateGranularity:
      description: |-
        The coarseness a [`GeneralizeDate`] reduces a date/timestamp to.

        Every rendering is an ISO-8601 form, so the output is locale-independent
        by construction — no localized month names or week markers to configure.
      oneOf:
        - description: |-
            Year only: `1987-03-14` → `1987`. The HIPAA Safe Harbor default —
            §164.514(b)(2)(i)(C) keeps year but drops finer date elements.
          type: string
          const: year
        - description: 'Year and month: `1987-03-14` → `1987-03`.'
          type: string
          const: year_month
        - description: |-
            Date down to the hour: `1987-03-14T09:32:15` → `1987-03-14T09`.
            A value with no time-of-day can't reduce to an hour and is declined.
          type: string
          const: hour
    DateStyle:
      description: |-
        Which written date convention a [`GeneralizeDate`] accepts on *input*.

        This governs only how the entity value is *parsed*; the output mirrors
        each value's own convention (see [`GeneralizeDate::render`]). The choice
        is explicit, never inferred from the entity's language: `03/04/1987` is
        a real date under both conventions (March 4 vs. April 3), so a wrong
        guess would silently emit a plausible-but-wrong month. The policy
        author, who knows the corpus's convention, sets it.
      oneOf:
        - description: |-
            ISO-8601 only: `1987-03-14`, `1987-03-14T09:32:15`. The unambiguous
            default.
          type: string
          const: iso
        - description: >-
            US month-first slashes: `MM/DD/YYYY` (`03/14/1987`), with an
            optional

            space-separated time (`03/14/1987 09:32:15`). ISO input is still

            accepted too, so a mixed corpus degrades gracefully.
          type: string
          const: us
    Category:
      description: >-
        The coarse group a [`Label`] belongs to, for organizing detected
        entities

        by kind (`financial`, `health`, `identity`, …).


        A category answers "what *sort* of information is this" at a display
        level:

        a consumer groups a redaction audit into sections by category. It is

        distinct from a label's [`tags`], which are cross-cutting sensitivity

        markers (`pii`, `phi`, `pci`) a label may carry several of; a label has
        at

        most **one** category. Built-in labels ship with a category; a custom

        [`Label`] has none unless one is set.


        The value is an open, lowercase `snake_case` identifier, so a custom
        label

        can define its own category rather than being confined to the shipped
        set.


        [`Label`]: super::Label

        [`tags`]: super::Label::tags
      type: string
    LocalizedText:
      description: |-
        A value localized per [`LanguageTag`], with an English-first fallback.

        The reusable mechanism behind any text that varies by language: a
        [`Label`]'s display name and description, a redaction operator's bucket
        label, and so on. English (`"en"`) is the conventional anchor —
        constructors seed it, and [`resolve`] falls back to it (then to any
        entry) when a requested locale is absent, so a caller that supplied
        English always gets *some* value.

        Generic over the stored value `T`, so it carries a bare `HipStr`
        (a bucket label) or a richer struct (a label's name-plus-description)
        equally. It is a thin wrapper over a `HashMap<LanguageTag, T>`; the
        added value is the fallback policy in [`resolve`], kept in one place
        rather than reimplemented at each use site.

        [`Label`]: crate::entity::Label
        [`resolve`]: LocalizedText::resolve
      type: object
      additionalProperties:
        $ref: '#/components/schemas/LabelLocale'
    ConfidenceThreshold:
      description: |-
        Minimum-confidence cutoff in the closed range `0.0..=1.0`.

        A [`Confidence`] at or above the threshold *passes*; below it is
        filtered out. Kept a separate type from [`Confidence`] so a cutoff
        can never be passed where a score is expected, or vice versa.

        [`Confidence`]: crate::primitive::Confidence
      type: number
      format: float
    LabelLocale:
      description: |-
        A label's human-facing text in one language: a display name and an
        optional fuller description.

        The `name` is a short, natural-language phrase (`"phone number"`) — the
        label a zero-shot NER model like GLiNER matches on, and the primary
        text an LLM prompt shows. The `description` is optional extra guidance
        for backends that consume it (GLiNER-2.0's bi-encoder, an LLM); leave
        it `None` when the name alone is clear.
      type: object
      properties:
        description:
          description: |-
            Optional fuller description, for description-capable backends
            (GLiNER-2.0, LLM). `None` when the name suffices.
          type: string
        name:
          description: |-
            Short natural-language display name (e.g. `"phone number"`). What a
            zero-shot NER model matches on and an LLM prompt surfaces.
          type: string
      required:
        - name

````