---
openapi: 3.1.0
servers:
- url: "{node}"
  description: "Any node of a deployment. Every node answers every endpoint. A node\
    \ that does not hold the index writer forwards the writes it receives, and answers\
    \ a search from the copy it holds."
  variables:
    node:
      default: http://localhost:8080
      description: "The origin a node is reached at, followed by the path prefix it\
        \ is served under if a proxy in front of it adds one. The tutorials and the\
        \ published images use `http://localhost:8080`. A deployment replaces it with\
        \ its own address."
components:
  schemas:
    AnalyzerDefinition:
      description: "Specifies how the text of a usage is analyzed, with exactly one\
        \ of `preset`, `custom`, or `named`. An analyzer chain describes the indexing\
        \ process. The engine derives the query analyzer from the indexing chain.\
        \ Components that select words by locale, such as stopwords and stemming,\
        \ use the locale of the value being analyzed unless you specify a locale.\
        \ See [Analysis](https://exofind.dev/reference/analysis/)."
      examples:
      - preset: full_text
      type: object
      properties:
        preset:
          description: A preset specifies a predefined analyzer chain. The engine
            expands the preset before storing the index definition.
          $ref: "#/components/schemas/Preset"
        custom:
          description: "A custom analyzer chain that defines character filters, a\
            \ tokenizer, and token filters."
          $ref: "#/components/schemas/Custom"
        named:
          type: string
          description: A named chain references an analyzer defined under `resources`
            in the index definition. Used to share analyzer configurations across
            fields. Validation fails if the specified name does not exist under `resources`.
          examples:
          - prose
    AndClause:
      required:
      - type
      - clauses
      description: Matches documents where all child clauses match.
      examples:
      - type: and
        clauses:
        - field: category
          match:
            value: fiction
        - field: published
          match:
            value: true
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - and
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Child clauses, all of which must match."
      type: object
    AnyMatcher:
      required:
      - type
      description: Matches any document that contains a value for the field.
      examples:
      - type: any
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - any
      type: object
    AsciiFolding:
      type: object
      description: Converts non-ASCII characters to ASCII equivalents.
      examples:
      - preserveOriginal: true
      properties:
        preserveOriginal:
          type: boolean
          description: Whether to preserve the original non-ASCII token alongside
            the folded one.
          default: false
    AuditedGeneration:
      type: object
      description: One generation as the registry and storage each describe it.
      examples:
      - name: "1"
        registered: true
        stored: synced
      properties:
        name:
          type: string
          description: The name of the generation.
          examples:
          - "1"
        registered:
          type: boolean
          description: A boolean indicating whether the registry names the generation.
        stored:
          description: "What storage holds under it. `synced`: storage holds a manifest;\
            \ nodes can pull and serve this generation. `incomplete`: storage holds\
            \ a prefix without a manifest (such as an unfinished push or what an interrupted\
            \ removal left of a deleted generation). `missing`: the generation is\
            \ registered, but nothing exists in storage."
          $ref: "#/components/schemas/Stored"
        removedAt:
          type: string
          description: "When the generation was deleted on its own, as an ISO 8601\
            \ timestamp. Present while its storage waits for the sweep that removes\
            \ it. A generation of a deleted index carries the index's `removedAt`\
            \ instead. Omitted otherwise."
          examples:
          - 2026-09-03T10:15:00Z
    AuditedIndex:
      type: object
      description: One index as the registry and storage each describe it.
      examples:
      - name: products
        registered: true
        live: "2"
        generations:
        - name: "1"
          registered: true
          stored: synced
        - name: "2"
          registered: true
          stored: synced
      properties:
        name:
          type: string
          description: Name of the index.
          examples:
          - products
        registered:
          type: boolean
          description: A boolean indicating whether the registry has an entry for
            the index.
        live:
          type: string
          description: The generation the index answers for. Omitted when unregistered
            or when no generation is live.
          examples:
          - "2"
        proposedLive:
          type: string
          description: "The generation that a repair with `promoteNewest` would make\
            \ live. Omitted when none would be promoted, which includes a deleted\
            \ index."
          examples:
          - "1"
        removedAt:
          type: string
          description: "When the index was deleted, as an ISO 8601 timestamp. Present\
            \ while the storage of the deleted index waits for the sweep that removes\
            \ it; a repair registers such an index only when asked to restore it.\
            \ Omitted otherwise."
          examples:
          - 2026-09-03T10:15:00Z
        generations:
          type: array
          items:
            $ref: "#/components/schemas/AuditedGeneration"
          description: "A list of generations found for the index, ordered by name."
    BooleanFieldDefinition:
      required:
      - type
      description: "Represents boolean values (`true` or `false`). A boolean has nothing\
        \ to analyze, so filtering is the only way to search it."
      examples:
      - type: boolean
        filter: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - boolean
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
      type: object
    BoostClause:
      required:
      - type
      - weight
      - clauses
      description: Increases the relevance score of documents that satisfy child clauses
        without excluding non-matching documents.
      examples:
      - type: boost
        weight: 2
        clauses:
        - field: featured
          match:
            value: true
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - boost
        weight:
          type: number
          format: float
          description: "Multiplier applied to matching documents. Values greater than\
            \ `1` increase score; values between `0` and `1` decrease score. Leaving\
            \ it out, or setting it below `0` or to a non-finite number, returns `search:clause:weight_out_of_range`."
          examples:
          - 2
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: Clauses required to apply the boost weight.
      type: object
    Candidate:
      type: object
      description: A node competing to write indexes.
      examples:
      - node: node-a-7f21
        address: http://node-a:8080
        expiresAt: 2026-08-21T10:15:30Z
      properties:
        node:
          type: string
          description: The name the node competes under.
          examples:
          - node-a-7f21
        address:
          type: string
          description: Target address for write forwarding. Omitted when the node
            did not set `EXOFIND_NODE_ADDRESS`.
          examples:
          - http://node-a:8080
        expiresAt:
          type: string
          description: "The timestamp when the candidacy expires unless renewed by\
            \ the node, as an ISO 8601 timestamp."
          examples:
          - 2026-08-21T10:15:30Z
    CharFilter:
      type: object
      description: A transformation of the raw text before tokenization. Specify exactly
        one character filter by including its configuration.
      examples:
      - htmlStrip: {}
      properties:
        htmlStrip:
          description: Strips HTML and XML markup and keeps text between tags.
          $ref: "#/components/schemas/HtmlStrip"
        mapping:
          description: Replaces occurrences of each key with its value.
          $ref: "#/components/schemas/MappingCharFilter"
        patternReplace:
          description: Replaces substrings that match a regular expression.
          $ref: "#/components/schemas/PatternReplace"
    Claim:
      type: object
      description: An index and the node writing it.
      examples:
      - index: products
        node: node-a-7f21
        address: http://node-a:8080
        expiresAt: 2026-08-21T10:15:30Z
      properties:
        index:
          type: string
          description: Name of the index.
          examples:
          - products
        node:
          type: string
          description: The node writing the index.
          examples:
          - node-a-7f21
        address:
          type: string
          description: Target address for write forwarding. Omitted when the node
            did not set `EXOFIND_NODE_ADDRESS`.
          examples:
          - http://node-a:8080
        expiresAt:
          type: string
          description: "The timestamp when the claim expires unless renewed by the\
            \ node, as an ISO 8601 timestamp."
          examples:
          - 2026-08-21T10:15:30Z
    Clause:
      oneOf:
      - $ref: "#/components/schemas/FieldClause"
      - $ref: "#/components/schemas/TextClause"
      - $ref: "#/components/schemas/KnnClause"
      - $ref: "#/components/schemas/NestedClause"
      - $ref: "#/components/schemas/AndClause"
      - $ref: "#/components/schemas/OrClause"
      - $ref: "#/components/schemas/NotClause"
      - $ref: "#/components/schemas/BoostClause"
      - $ref: "#/components/schemas/FuseClause"
      description: "A search condition, structured as a tagged union where `type`\
        \ selects the clause type. The engine also accepts a clause that omits `type`,\
        \ which it reads as a `field` clause containing `field` and `match`. See [Clauses](https://exofind.dev/reference/search-api/#clauses)."
      examples:
      - field: category
        match:
          value: fiction
      discriminator:
        propertyName: type
        mapping:
          field: "#/components/schemas/FieldClause"
          text: "#/components/schemas/TextClause"
          knn: "#/components/schemas/KnnClause"
          nested: "#/components/schemas/NestedClause"
          and: "#/components/schemas/AndClause"
          or: "#/components/schemas/OrClause"
          not: "#/components/schemas/NotClause"
          boost: "#/components/schemas/BoostClause"
          fuse: "#/components/schemas/FuseClause"
    Collation:
      description: "Collation order for string comparisons: `locale` orders by the\
        \ rules of the locale so characters such as `å` sort in expected language\
        \ order; `binary` orders by byte order, which is faster for plain ASCII text."
      type: string
      enum:
      - binary
      - locale
    Combine:
      description: "Scope for multi-field term matching: `term` or `field`."
      type: string
      enum:
      - term
      - field
    CreatedKey:
      description: A newly created key. This is the only response its credential ever
        appears in.
      examples:
      - credential: exok_4ff6b760264c1918_ePQcdT1O9HSATZoXfDbT8hhHGsP9VpZH
        key:
          id: 4ff6b760264c1918
          description: the search backend
          grants:
          - permissions:
            - indexes.read
            - search
            indexes:
            - products
          createdAt: 2026-08-16T12:09:33.198275Z
          expiresAt: 2027-01-01T00:00:00Z
      type: object
      properties:
        credential:
          type: string
          description: "The generated credential, presented as `Authorization: Bearer`.\
            \ Only a hash of it is stored, so this response is the only chance to\
            \ keep it. A lost credential cannot be recovered and must be replaced."
          examples:
          - exok_4ff6b760264c1918_ePQcdT1O9HSATZoXfDbT8hhHGsP9VpZH
        key:
          description: The key as a listing will show it from now on.
          $ref: "#/components/schemas/KeyInfo"
    Custom:
      type: object
      description: "A custom analyzer chain that defines character filters, a tokenizer,\
        \ and token filters. Each component is an object with one key that specifies\
        \ the component type, for example `{ \"whitespace\": {} }`."
      examples:
      - charFilters:
        - htmlStrip: {}
        tokenizer:
          icu: {}
        filters:
        - normalize: {}
        - stemming:
            locale: sv
      properties:
        charFilters:
          type: array
          items:
            $ref: "#/components/schemas/CharFilter"
          description: "An array of character filters applied to the raw text before\
            \ tokenization, in order."
        tokenizer:
          description: "The tokenizer that splits text into tokens. If omitted, the\
            \ engine chooses a tokenizer based on the locale of the value (Unicode\
            \ segmentation for most locales; language-specific segmentation for Chinese,\
            \ Japanese, and Korean)."
          $ref: "#/components/schemas/Tokenizer"
        filters:
          type: array
          items:
            $ref: "#/components/schemas/TokenFilter"
          description: "An array of token filters applied to tokens, in order."
    Decay:
      type: object
      description: "Ranks a timestamp by how long ago it was, halving every half life."
      examples:
      - halfLife: 604800
      required:
      - halfLife
      properties:
        halfLife:
          type: integer
          format: int64
          exclusiveMinimum: 0
          description: How many seconds it takes for the signal to be worth half as
            much. Must be above zero.
          examples:
          - 604800
    DeclaredValue:
      type: object
      description: "One value of a field, as stored, with the order a facet sorts\
        \ it by and the label a search answers it with per locale."
      examples:
      - value: S
        order: 1
        labels:
          en: Small
          sv: Liten
      properties:
        value:
          type: string
          description: "The value as the field stores it, which is what a facet counts\
            \ and a filter matches. Required and unique within the field; otherwise\
            \ the request returns `settings:fields:values_invalid`."
          examples:
          - S
        order:
          type: integer
          format: int32
          description: "Where the value sits in a facet ordered by `declared`, lower\
            \ first; values sharing an order are sorted by count. If omitted, the\
            \ value is ordered by count after every value that has an order, so labels\
            \ can be declared without an order."
          examples:
          - 1
        labels:
          type: object
          additionalProperties:
            type: string
          description: "What a person reads instead of the value, keyed by BCP-47\
            \ tag in canonical form (`sv`, `en-GB`). A search answers the label of\
            \ its locale, matched as closely as the tags tell apart, and the label\
            \ of the field's default locale where its own has none. A tag that is\
            \ not canonical, or a blank label, returns `settings:fields:values_invalid`."
    Decompound:
      description: Controls compound word splitting in the engine-generated analyzer
        chain. Set to `none` to disable splitting.
      type: string
      enum:
      - none
    DecompoundFilter:
      type: object
      description: "Splits compound words into parts and retains the original compound\
        \ word. See [Compound words](https://exofind.dev/reference/analysis/#compound-words).\
        \ If omitted, uses the dictionary for the locale of the value. Applied at\
        \ index time."
      examples:
      - locale: sv
      properties:
        locale:
          type: string
          description: "BCP-47 locale whose rules and dictionary split the words.\
            \ Omitted, the locale of the value being analyzed is used; a value in\
            \ a locale the engine has no decompounding data for passes through unsplit."
          examples:
          - sv
    DeleteRequest:
      description: "Which documents to remove. The request must name exactly one of\
        \ `keys`, `query`, and `all` (`document:delete:target_required`, `document:delete:target_conflicting`)."
      examples:
      - keys:
        - "1"
        - "2"
      - query:
        - field: category
          match:
            value: sylt
      - all: true
      type: object
      properties:
        keys:
          type: array
          items: {}
          description: "List of primary keys to delete, formatted according to the\
            \ key field type. All keys are validated before any documents are removed;\
            \ if any key is invalid, no documents are removed. An empty array deletes\
            \ nothing, and requesting the deletion of an unindexed key produces a\
            \ success response."
        query:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Query clauses matching documents to delete, using search query\
            \ clause syntax. Removes matching committed searchable documents and any\
            \ uncommitted documents indexed since the last commit. The array requires\
            \ at least one clause (`document:delete:query_empty`). To empty the index,\
            \ send `all` instead."
        all:
          type: boolean
          description: Set to `true` to remove every document and empty the index.
            Cannot be combined with `keys` or `query`.
        locale:
          type: string
          description: "BCP 47 locale tag used to match locale-specific fields, defaulting\
            \ to each field's default locale. Valid only when specifying `query` (`document:delete:locale_without_query`)."
          examples:
          - sv
    DeleteResponse:
      description: The count of deleted documents.
      examples:
      - deleted: 3
        freshness: AQoIcHJvZHVjdHMSATIYBw
      type: object
      properties:
        deleted:
          type: integer
          format: int32
          description: "How many documents were removed. For requests using `keys`,\
            \ this is the number of keys provided in the request, since requesting\
            \ the deletion of an unindexed key produces a success response. For requests\
            \ using `query`, this is the number of matching committed searchable documents."
          examples:
          - 3
        freshness:
          type: string
          description: "A freshness token for the state the change lands in. Pass\
            \ it as `freshness.atLeast` on a search, or in the `X-Exofind-Freshness`\
            \ header of a read, and that request is answered only once the node holds\
            \ the change. Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    Direction:
      description: "Which end of a tie-breaker field wins: `ascending` or `descending`."
      type: string
      enum:
      - ascending
      - descending
    DistanceMatcher:
      required:
      - type
      - lat
      - lon
      - radius
      description: Matches geopoint values within `radius` meters of the specified
        latitude and longitude coordinates.
      examples:
      - type: distance
        lat: 59.3
        lon: 18.1
        radius: 5000
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - distance
        lat:
          type: number
          format: double
          maximum: 90
          minimum: -90
          description: "Latitude of the origin, in degrees."
          examples:
          - 59.3
        lon:
          type: number
          format: double
          maximum: 180
          minimum: -180
          description: "Longitude of the origin, in degrees."
          examples:
          - 18.1
        radius:
          type: number
          format: double
          description: Maximum distance from the origin in meters.
          examples:
          - 5000
      type: object
    DistanceSort:
      required:
      - type
      - field
      - lat
      - lon
      description: "Sorts by distance from the specified geographic coordinate, nearest\
        \ first. Accepts no `order` property. A distance sort on a nested object field\
        \ returns `search:sort:nested_unsupported`."
      examples:
      - type: distance
        field: location
        lat: 59.3
        lon: 18.1
      properties:
        type:
          description: Selects the sort type.
          type: string
          enum:
          - distance
        field:
          type: string
          description: "Target geopoint field, as named in the index definition."
          examples:
          - location
        lat:
          type: number
          format: double
          maximum: 90
          minimum: -90
          description: "Latitude of the origin, in degrees."
          examples:
          - 59.3
        lon:
          type: number
          format: double
          maximum: 180
          minimum: -180
          description: "Longitude of the origin, in degrees."
          examples:
          - 18.1
      type: object
    DocumentFailure:
      description: "One entry of a batch the index refused, reported by a request\
        \ sent with `?onError=skip`. The entry is named by `position` for a caller\
        \ walking the list it sent, and also by `line` for a caller pointing at a\
        \ newline-delimited file."
      examples:
      - position: 3
        line: 4
        errors:
        - code: document:field_unknown
          message: Field `nonexistent` does not exist in index
          path: "documents[3].nonexistent"
          arguments:
            position: "3"
            processed: "3"
            name: nonexistent
      type: object
      properties:
        position:
          type: integer
          format: int32
          description: "Which entry of the batch this was, counted from zero. Matches\
            \ the index in the `path` of each error."
          examples:
          - 3
        line:
          type: integer
          format: int32
          description: "The line of the request body the entry starts on, counted\
            \ from one. Present only for a newline-delimited body; a body that carries\
            \ the entries in a `documents` array omits it."
          examples:
          - 4
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ErrorDetail"
          description: "Everything wrong with the entry, in the shape the `errors`\
            \ of a failed request take."
    DocumentResponse:
      description: "One document read from an index by its primary key. The read is\
        \ answered from a point-in-time snapshot and sees committed data only, so\
        \ an uncommitted write is not visible. For more information, see [Reading\
        \ one document](https://exofind.dev/reference/documents-api/#reading-one-document)."
      examples:
      - document:
          id: "1"
          name:
            sv: blåbärssylt
          energy: 234
        freshness: AQoIcHJvZHVjdHMSATIYBw
      type: object
      properties:
        document:
          description: "The document, formatted as originally indexed. Send it back\
            \ to `POST /v1alpha1/indexes/{name}/documents` to index it again."
          type: object
        freshness:
          type: string
          description: "A freshness token for the state the document was read from.\
            \ Pass it in the `X-Exofind-Freshness` header of the next request, and\
            \ that request is answered from this state or a later one whichever node\
            \ it lands on. Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    DocumentsRequest:
      description: "Documents to index. A document specifies its own primary key.\
        \ Indexing a document with an existing key replaces the document under that\
        \ key; if an index definition does not declare a primary key, each request\
        \ adds a new document. See [How a document is shaped](https://exofind.dev/reference/documents-api/#how-a-document-is-shaped)."
      examples:
      - documents:
        - id: "1"
          name:
            sv: blåbärssylt
            en: blueberry jam
          tags:
          - sylt
          - bär
          energy: 234
      type: object
      required:
      - documents
      properties:
        documents:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: "The documents, keyed by field name. A field declared `multiple`\
            \ is an array, a locale-specific field an object keyed by locale tag,\
            \ a geo point an object with `lat` and `lon` fields, a vector an array\
            \ of numbers, an object field a nested JSON object, and a timestamp an\
            \ ISO 8601 string. A field set to `null` is treated as omitted."
    DocumentsResponse:
      description: "The count of indexed documents, and any that were skipped."
      examples:
      - indexed: 2
        failed: []
        freshness: AQoIcHJvZHVjdHMSATIYBw
      type: object
      properties:
        indexed:
          type: integer
          format: int32
          description: "The number of documents indexed. For a successful request,\
            \ this includes every document in the request, except any reported under\
            \ `failed`."
          examples:
          - 2
        failed:
          type: array
          items:
            $ref: "#/components/schemas/DocumentFailure"
          description: "The documents the index refused, in the order sent. Holds\
            \ entries only when the request is sent with `?onError=skip`; a request\
            \ sent without it fails on the first refused document. Always present,\
            \ and empty when nothing was skipped."
        freshness:
          type: string
          description: "A freshness token for the state the change lands in. Pass\
            \ it as `freshness.atLeast` on a search, or in the `X-Exofind-Freshness`\
            \ header of a read, and that request is answered only once the node holds\
            \ the change. Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    DoubleFieldDefinition:
      required:
      - type
      description: "Represents a 64-bit floating-point number. A number has nothing\
        \ to analyze, so it is searched by filtering, which supports both exact matches\
        \ and range queries."
      examples:
      - type: double
        filter: {}
        sort: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - double
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
        signal:
          description: "Makes the field a ranking signal that is refreshed in place\
            \ through the document update action, without indexing the document again.\
            \ A signal field is sortable and is returned in results, but is left out\
            \ of the document source and cannot be combined with `filter`, `facet`,\
            \ `stored`, `multiple`, `locales` or `primaryKey`. A document indexed\
            \ without a value keeps the value the field holds. See [Signal fields](https://exofind.dev/reference/field-types/#signal-fields)."
          $ref: "#/components/schemas/SignalUsage"
        validation:
          description: Sets allowed numeric bounds. Documents containing values outside
            these bounds are rejected.
          $ref: "#/components/schemas/DoubleValidation"
        unit:
          type: string
          description: "What the values are measured in: an ISO 4217 currency code\
            \ such as `SEK`, a CLDR unit identifier such as `kilogram` or `gigabyte`,\
            \ or any other text matched as written. A search in `user` mode reads\
            \ a number typed next to the unit, or next to a comparative word such\
            \ as `under`, as a filter on this field. Changing the unit does not require\
            \ a reindex. See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          examples:
          - EUR
      type: object
    DoubleValidation:
      type: object
      description: The allowed numeric bounds for a `double` field.
      examples:
      - min: 0
        max: 1000
      properties:
        min:
          type: number
          format: double
          description: Lowest value accepted.
        max:
          type: number
          format: double
          description: Highest value accepted.
    Dropped:
      type: object
      description: Details of a single dropped query word and the reason for its removal.
      examples:
      - word: waterproof
        reason: unmatched
      properties:
        word:
          type: string
          description: The word as it was typed.
          examples:
          - waterproof
        reason:
          description: Reason the word was dropped.
          $ref: "#/components/schemas/Reason"
    EdgeNgram:
      type: object
      description: Generates prefix n-grams for tokens.
      examples:
      - minGram: 1
        maxGram: 20
      properties:
        minGram:
          type: integer
          format: int32
          description: The shortest prefix to index.
          default: 1
        maxGram:
          type: integer
          format: int32
          description: The longest prefix to index.
          default: 20
    EqualsMatcher:
      required:
      - type
      - value
      description: Matches field values equal to `value`.
      examples:
      - value: fiction
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - equals
        value:
          description: The value that the field value must equal.
          examples:
          - fiction
      type: object
    ErrorDetail:
      type: object
      description: One problem found in a request.
      examples:
      - code: index:field:primary_key:multiple_unsupported
        message: "Field `id` is marked as a primary key and multiple, primary keys\
          \ can not have multiple values"
        path: id
      properties:
        code:
          type: string
          description: The error code identifying this specific problem.
          examples:
          - index:field:primary_key:multiple_unsupported
        message:
          type: string
          description: Human-readable description of this problem.
          examples:
          - "Field `id` is marked as a primary key and multiple, primary keys can\
            \ not have multiple values"
        path:
          type: string
          description: "Location of the offending value in the request, such as `fields.title`\
            \ or `documents[1].nonexistent`. Names join with `.`, one element of a\
            \ list reads `[n]` counted from zero, and a key of a free-form map such\
            \ as `metadata` goes in brackets and double quotes when it holds a dot\
            \ or a bracket, as `metadata[\"build.sha\"]`. A field inside an `object`\
            \ field carries its own dotted path, so a path here reaches a field the\
            \ same way a query does. Omitted when the problem applies to the request\
            \ as a whole. See [API conventions](https://exofind.dev/reference/api-conventions/)."
          examples:
          - id
        arguments:
          type: object
          additionalProperties:
            type: string
          description: "The values the message was rendered with, so a client can\
            \ render a message of its own from the code."
    ErrorResponse:
      description: "The body of every failed request. Error codes use colon-separated\
        \ namespaces such as `index:field:name_invalid`, are stable across API versions,\
        \ and are never renamed or reused, so clients match on `code` rather than\
        \ on `message`. See [Errors](https://exofind.dev/reference/errors/)."
      examples:
      - code: validation
        message: Request contains 1 error
        errors:
        - code: index:field:primary_key:multiple_unsupported
          message: "Field `id` is marked as a primary key and multiple, primary keys\
            \ can not have multiple values"
          path: id
      type: object
      properties:
        code:
          type: string
          description: "Identifies the failure type. For validation failures, this\
            \ is `validation` and the individual problems are listed in `errors`.\
            \ For all other failures, this matches the `code` of the single entry\
            \ in `errors`."
          examples:
          - validation
        message:
          type: string
          description: "Human-readable message for log output. Match on `code` rather\
            \ than on `message`. When a validation failure contains one problem, this\
            \ is that problem's message. When it contains several, it reads `Request\
            \ contains N errors`."
          examples:
          - Request contains 2 errors
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ErrorDetail"
          description: "All problems found in the request. A validation failure reports\
            \ every field with a problem, so a caller can fix them in one pass rather\
            \ than one request at a time."
    Exact:
      type: object
      description: Configures score boosting for queries matching the full field value.
      examples:
      - boost: 2
      properties:
        boost:
          type: number
          format: float
          description: Score boost multiplier applied when a query matches the full
            field value.
          default: 2
    ExplainDetail:
      type: object
      description: One score step in the explanation tree.
      examples:
      - matched: true
        score: 8.42
        description: "weight(name:spring) [BM25], result of:"
        clause: "query[0]"
        clauseType: text
        field: name
        usage: matching
      properties:
        matched:
          type: boolean
          description: "Whether this step was satisfied. A non-matching step contributes\
            \ nothing to the parent score, and its `children` describe the reason."
        score:
          type: number
          format: float
          description: Score contributed by this step to its parent step. Returns
            0 if the step did not match.
        description:
          type: string
          description: Human-readable explanation of the step.
          examples:
          - "weight(title:bok) [BM25], result of:"
        clause:
          type: string
          description: Path to the clause in the request body that produced this step.
            Omitted when the step is not an individual clause.
          examples:
          - "query[0].clauses[2]"
        clauseType:
          type: string
          description: Clause type matching request syntax in `type`. Omitted when
            `clause` is omitted.
          examples:
          - text
        field:
          type: string
          description: Index definition field name evaluated by the step. Omitted
            when the step reads no fields or multiple fields.
          examples:
          - title
        usage:
          type: string
          description: Field usage mode evaluated by the step (such as `matching`
            or `filter`). Omitted when `field` is omitted.
          examples:
          - matching
        locale:
          type: string
          description: BCP 47 tag of the variant this step reads. Omitted for a field
            that holds one variant for every language.
          examples:
          - sv
        children:
          type: array
          items:
            $ref: "#/components/schemas/ExplainDetail"
          description: Child score steps that compose this step. Empty for leaf steps.
    ExplainResponse:
      description: "Explains how a specific document or value hit scores for a search\
        \ query. See [Explaining a result](https://exofind.dev/reference/search-api/#explaining-a-result)."
      examples:
      - matched: true
        score: 8.42
        detail:
          matched: true
          score: 8.42
          description: "sum of:"
          children:
          - matched: true
            score: 8.42
            description: "weight(name:spring) [BM25], result of:"
            clause: "query[0]"
            clauseType: text
            field: name
            usage: matching
        generation: "2"
        tookMs: 1.208
      type: object
      properties:
        matched:
          type: boolean
          description: Whether the hit satisfies the search. A hit that does not match
            appears in no search results.
        score:
          type: number
          format: float
          description: The relevance score of the hit. Returns `0` if the hit does
            not match.
          examples:
          - 7.42
        detail:
          description: Root score step explaining how the score was calculated.
          $ref: "#/components/schemas/ExplainDetail"
        relaxed:
          description: "Relaxation details containing dropped words and the effective\
            \ query text. Omitted if query relaxation did not run. When zero results\
            \ trigger query relaxation, the explanation tree reflects the relaxed\
            \ query that executed."
          $ref: "#/components/schemas/Relaxed"
        interpreted:
          description: "What the search read out of the query text as filters, and\
            \ the text that was left. Omitted when nothing was read. The explanation\
            \ tree reflects the search with the filters in it."
          $ref: "#/components/schemas/Interpreted"
        generation:
          type: string
          description: "Name of the generation that answered. A request that names\
            \ the index answers from the generation that is live when it arrives,\
            \ so add `@` and this name to the index name to send a later request to\
            \ the same data. See [Names and generations](https://exofind.dev/reference/admin-api/#names-and-generations)."
          examples:
          - "2"
        freshness:
          type: string
          description: "A freshness token for the state the answer came from: the\
            \ generation, its commit, and the version of the search settings. Pass\
            \ it as `freshness.atLeast` on a later request, and that request is answered\
            \ from this state or a later one whichever node it lands on. Opaque; pass\
            \ it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
        tookMs:
          type: number
          format: double
          description: "Execution time for the explanation in milliseconds, including\
            \ fractions of one. An explanation compiles and runs the search the way\
            \ a search does, so it costs what a search costs."
          examples:
          - 1.208
    FacetBucket:
      type: object
      description: One range bucket of a faceted field with its match count.
      examples:
      - from: 100
        to: 200
        count: 17
      properties:
        from:
          description: "Inclusive lower bound of the range bucket, as specified in\
            \ the request. Omitted for open-ended ranges."
          examples:
          - 100
        to:
          description: "Exclusive upper bound of the range bucket, as specified in\
            \ the request. Omitted for open-ended ranges."
          examples:
          - 200
        count:
          type: integer
          format: int64
          description: Number of matching documents with values falling within the
            range bucket.
          examples:
          - 17
    FacetOrder:
      description: "Sort order of facet values: `count` (descending by count), `value`\
        \ (ascending by value), or `declared` (the order the search settings declare\
        \ for the field's values, followed by every other value by count)."
      type: string
      enum:
      - count
      - value
      - declared
    FacetRange:
      type: object
      description: "One range bucket, holding values from `from` (inclusive) up to\
        \ `to` (exclusive), so adjacent buckets sharing a bound count no value twice.\
        \ Either bound may be omitted for an open-ended range, but not both (`search:facet:range_empty`),\
        \ and `to` must be greater than `from` (`search:facet:range_invalid`). At\
        \ most 1000 buckets per facet (`search:facet:ranges_too_many`); using `ranges`\
        \ on an unsupported field type returns `search:matcher:type_unsupported`."
      examples:
      - from: 100
        to: 200
      properties:
        from:
          description: "The lowest value the bucket holds, itself included. Omit for\
            \ no lower end."
          examples:
          - 100
        to:
          description: "Where the bucket ends, itself not included. Omit for no upper\
            \ end."
          examples:
          - 200
    FacetRequest:
      type: object
      description: "Computes match counts for distinct values of a field. The target\
        \ field must have `facet` enabled in its field definition; otherwise, the\
        \ request returns `search:usage_unsupported`."
      examples:
      - field: category
        limit: 20
        order: count
      properties:
        name:
          type: string
          description: Key used for the facet in the response. Required when faceting
            on the same field multiple times. Duplicate facet names return `search:facet:name_duplicate`.
            Defaults to the field name.
        field:
          type: string
          description: Target field to aggregate.
          examples:
          - category
        limit:
          type: integer
          format: int32
          minimum: 1
          description: "Maximum number of facet values to return, at most `EXOFIND_SEARCH_MAX_FACET_VALUES`."
          default: 10
        order:
          description: "Sort order of facet values: `\"count\"` (descending by count),\
            \ `\"value\"` (ascending by value), or `\"declared\"` (the order the search\
            \ settings declare for the field's values, followed by every other value\
            \ by count)."
          default: count
          $ref: "#/components/schemas/FacetOrder"
        ranges:
          type: array
          items:
            $ref: "#/components/schemas/FacetRange"
          description: "Array of range bucket definitions. See [Range buckets](https://exofind.dev/reference/search-api/#range-buckets).\
            \ Cannot be combined with `limit` or `order` (`search:facet:ranges_conflicting`)."
        path:
          type: string
          description: "Starting path level for hierarchical fields. See [Counting\
            \ down a tree](https://exofind.dev/reference/search-api/#counting-down-a-tree).\
            \ Defaults to the root."
        depth:
          type: integer
          format: int32
          maximum: 10
          minimum: 1
          description: Number of hierarchical levels below `path` to count.
          default: 1
        excludeFilters:
          type: array
          items:
            type: string
          description: "List of field paths whose filter entries are excluded from\
            \ this facet's calculation. Defaults to the facet's own field path. An\
            \ empty array `[]` disables filter exclusion. A blank path returns `search:facet:exclude_filters_invalid`."
      required:
      - field
    FacetResult:
      type: object
      description: "Match counts for one faceted field. Counting per value returns\
        \ `values` with `totalValues`; counting into ranges returns `buckets`, omitting\
        \ the other representation. Facet counts exclude filter entries on the facet's\
        \ own field by default."
      examples:
      - values:
        - value: fiction
          count: 87
        - value: poetry
          count: 41
        totalValues: 2
      properties:
        values:
          type: array
          items:
            $ref: "#/components/schemas/FacetValue"
          description: "Array of facet value objects, in the requested order and limited\
            \ to the configured maximum."
        totalValues:
          type: integer
          format: int32
          description: "Total count of distinct values matching the query. Counts\
            \ values, not documents. Exceeds the number of entries under `values`\
            \ when the limit is reached, and no count covers the values left out."
        buckets:
          type: array
          items:
            $ref: "#/components/schemas/FacetBucket"
          description: "Array of range bucket objects with match counts, in the requested\
            \ order."
    FacetUsage:
      type: object
      description: Enables value count aggregations across search results. Carries
        no configuration options.
      examples:
      - {}
    FacetValue:
      type: object
      description: "One value of a faceted field with its match count. For hierarchical\
        \ fields, returns an entry per hierarchy level and nests child levels under\
        \ `values`. Other fields omit hierarchical properties."
      examples:
      - value: fiction
        count: 87
        label: Fiction
      properties:
        value:
          description: "The facet value in its stored format: a string, boolean, number,\
            \ or ISO 8601 timestamp string. For a hierarchical field, returns the\
            \ label of the current level."
          examples:
          - fiction
        count:
          type: integer
          format: int64
          description: Number of matching documents containing this value.
          examples:
          - 87
        label:
          type: string
          description: "The label the search settings of the index declare for the\
            \ value, in the locale of the search, falling back to the field's default\
            \ locale. Omitted when the settings declare no label for the value. See\
            \ [Field settings](https://exofind.dev/reference/admin-api/#field-settings)."
          examples:
          - Fiction
        path:
          type: string
          description: "The full path to the level, used in `under` filter matchers.\
            \ Omitted for non-hierarchical fields."
          examples:
          - Men/Shoes
        values:
          type: array
          items:
            $ref: "#/components/schemas/FacetValue"
          description: "Child hierarchy levels with their counts, evaluated up to\
            \ `depth` levels below the current path. Omitted at the maximum counted\
            \ depth and for non-hierarchical fields."
        totalValues:
          type: integer
          format: int32
          description: "Total count of distinct child values below this level. Counts\
            \ values, not documents. Exceeds the number of entries under `values`\
            \ when the limit is reached, and no count covers the values left out.\
            \ Omitted for non-hierarchical fields."
    FacetValuesRequest:
      description: "Asks for the values of one facet field that start with a prefix,\
        \ counted under the query and filters of a search. All properties are optional;\
        \ an empty request answers the most common values under everything the index\
        \ holds."
      examples:
      - query:
        - type: text
          text: running shoes
        filters:
        - field: brand
          match:
            type: in
            values:
            - Nike
        prefix: adi
        limit: 5
      type: object
      properties:
        query:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses that a counted document must satisfy, in the same\
            \ shape as the `query` of a search. If omitted, every document is counted."
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Refinement clauses, in the same shape as the `filters` of\
            \ a search. Filter entries on the facet's own field are left out of the\
            \ counts, so a value already ticked keeps the other values countable (see\
            \ [Facets](https://exofind.dev/reference/search-api/#facets))."
        prefix:
          type: string
          description: "What the answered values start with. Compared with the values\
            \ folded in case and Unicode form, so `rö` finds `Röd`, and with the labels\
            \ the search settings declare for them in the locale of the request. A\
            \ number, boolean or timestamp field compares the prefix with the value\
            \ as a search response shows it, ignoring case. If omitted or blank, every\
            \ value is answered."
          examples:
          - adi
        locale:
          type: string
          description: "BCP-47 locale tag used to read locale-specific fields, as\
            \ for a search. If omitted, uses each field's default locale."
          examples:
          - sv
        limit:
          type: integer
          format: int32
          minimum: 1
          description: "Maximum number of values to return, at most `EXOFIND_SEARCH_MAX_FACET_VALUES`."
          default: 10
        order:
          description: "Sort order of the values: `\"count\"` (descending by count),\
            \ `\"value\"` (ascending by value), or `\"declared\"` (the order the search\
            \ settings declare for the field's values, followed by every other value\
            \ by count)."
          default: count
          $ref: "#/components/schemas/FacetOrder"
        freshness:
          description: "What the request demands of the state it is answered from.\
            \ Omit it to be answered from what the node holds. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          $ref: "#/components/schemas/Freshness"
    FacetValuesResponse:
      description: "The values of one facet field that start with the prefix, each\
        \ with how many matching documents hold it."
      examples:
      - values:
        - value: adidas
          count: 87
        - value: Adidas Originals
          count: 12
        totalValues: 2
        generation: "2"
        tookMs: 1.208
      type: object
      properties:
        values:
          type: array
          items:
            $ref: "#/components/schemas/FacetValue"
          description: "The values with their counts, in the requested order and limited\
            \ to the configured maximum."
        totalValues:
          type: integer
          format: int32
          description: "Total count of distinct values that start with the prefix.\
            \ Counts values, not documents. Exceeds the number of entries under `values`\
            \ when the limit is reached, and no count covers the values left out."
        generation:
          type: string
          description: "Name of the generation that answered. A request that names\
            \ the index answers from the generation that is live when it arrives,\
            \ so add `@` and this name to the index name to send a later request to\
            \ the same data. See [Names and generations](https://exofind.dev/reference/admin-api/#names-and-generations)."
          examples:
          - "2"
        freshness:
          type: string
          description: "A freshness token for the state the answer came from: the\
            \ generation, its commit, and the version of the search settings. Pass\
            \ it as `freshness.atLeast` on a later request, and that request is answered\
            \ from this state or a later one whichever node it lands on. Opaque; pass\
            \ it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
        tookMs:
          type: number
          format: double
          description: "Execution time for the request in milliseconds, including\
            \ fractions of one."
          examples:
          - 1.208
    Fallback:
      description: "Controls whether a field participates in the index's locale fallback:\
        \ `enabled` populates missing locales from fallback values, and `disabled`\
        \ excludes the field from fallback resolution."
      type: string
      enum:
      - enabled
      - disabled
    FieldClause:
      required:
      - type
      - field
      - match
      description: "Matches documents by the value of a single field. The targeted\
        \ field must be indexed for the requested matcher usage; if it is not configured\
        \ for that usage, the request returns `search:usage_unsupported`."
      examples:
      - field: category
        match:
          value: fiction
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - field
        field:
          type: string
          description: "Target field, as named in the index definition."
          examples:
          - category
        match:
          description: Criteria evaluated against the field's values.
          $ref: "#/components/schemas/Matcher"
      type: object
    FieldDefinition:
      oneOf:
      - $ref: "#/components/schemas/StringFieldDefinition"
      - $ref: "#/components/schemas/BooleanFieldDefinition"
      - $ref: "#/components/schemas/VectorFieldDefinition"
      - $ref: "#/components/schemas/Int32FieldDefinition"
      - $ref: "#/components/schemas/Int64FieldDefinition"
      - $ref: "#/components/schemas/FloatFieldDefinition"
      - $ref: "#/components/schemas/DoubleFieldDefinition"
      - $ref: "#/components/schemas/TimestampFieldDefinition"
      - $ref: "#/components/schemas/GeoPointFieldDefinition"
      - $ref: "#/components/schemas/ObjectFieldDefinition"
      description: "Definition of a field, structured as a tagged union where `type`\
        \ selects the field type and the properties available on it. Field usages\
        \ are opt-in: adding an empty configuration object enables a usage with engine\
        \ defaults, and only explicitly configured properties are stored, preserving\
        \ default values across engine updates. See [Field types](https://exofind.dev/reference/field-types/)."
      examples:
      - type: string
        stored: true
        filter: {}
        matching:
          highlight: {}
      discriminator:
        propertyName: type
        mapping:
          string: "#/components/schemas/StringFieldDefinition"
          boolean: "#/components/schemas/BooleanFieldDefinition"
          vector: "#/components/schemas/VectorFieldDefinition"
          int32: "#/components/schemas/Int32FieldDefinition"
          int64: "#/components/schemas/Int64FieldDefinition"
          float: "#/components/schemas/FloatFieldDefinition"
          double: "#/components/schemas/DoubleFieldDefinition"
          timestamp: "#/components/schemas/TimestampFieldDefinition"
          geo_point: "#/components/schemas/GeoPointFieldDefinition"
          object: "#/components/schemas/ObjectFieldDefinition"
    FieldSettings:
      type: object
      description: How searches read one field. Every capability is off unless its
        object is present; an empty object turns it on with the engine defaults.
      examples:
      - interpret: {}
        suggest: {}
        values:
        - value: S
          order: 1
          labels:
            en: Small
            sv: Liten
      properties:
        interpret:
          description: "Reads the values the field holds out of the query text of\
            \ a search in `user` mode, as a filter on the field. The field must be\
            \ a `string` field with `filter` and `facet` and without `hierarchy`;\
            \ otherwise the request returns `settings:fields:interpret_unsupported`.\
            \ Carries no options. See [Reading the values of a field](https://exofind.dev/reference/search-api/#reading-the-values-of-a-field)."
          $ref: "#/components/schemas/InterpretUsage"
        values:
          type: array
          items:
            $ref: "#/components/schemas/DeclaredValue"
          description: "Values of the field with a declared `order` and `labels` per\
            \ locale. A facet with `\"order\": \"declared\"` answers these values\
            \ first, by `order`, and every other value after them by count. A facet\
            \ answers each value's `label` in the locale of the search, a prefix search\
            \ of the facet matches labels as well as values, and a search in `user`\
            \ mode with `interpret` on the field reads a typed label as its value.\
            \ The field must be a `string` field with `facet` and without `hierarchy`;\
            \ otherwise the request returns `settings:fields:values_unsupported`.\
            \ At most 10000 values per field. See [Field settings](https://exofind.dev/reference/admin-api/#field-settings)."
        suggest:
          description: "Suggests the values the field holds while a search is typed,\
            \ through `POST /v1alpha1/indexes/{name}/suggest`. The field must be a\
            \ `string` field with `facet` and without `hierarchy`; otherwise the request\
            \ returns `settings:fields:suggest_unsupported`. Carries no options. See\
            \ [Suggesting what to search for](https://exofind.dev/reference/search-api/#suggesting-what-to-search-for)."
          $ref: "#/components/schemas/Suggest"
    FieldSort:
      required:
      - type
      - field
      description: "Sorts by field value. The target field must have sorting enabled.\
        \ A field inside a `nested` [object](https://exofind.dev/reference/field-types/#object)\
        \ is named by its dotted path, and only the nested values that the query's\
        \ `nested` clauses matched are considered."
      examples:
      - field: name
        order: asc
      properties:
        type:
          description: Selects the sort type.
          type: string
          enum:
          - field
        field:
          type: string
          description: "Target field, as named in the index definition."
          examples:
          - name
        order:
          description: Direction to order in.
          default: asc
          $ref: "#/components/schemas/SortOrder"
      type: object
    Filter:
      type: object
      description: One filter read out of the query text.
      examples:
      - field: price
        match:
          type: range
          lt: 500
        words:
        - under
        - "500"
      properties:
        field:
          type: string
          description: The field the filter is on.
          examples:
          - price
        when:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses that hold where the filter is read, as the `when`\
            \ of the target the request named. Absent when the filter is read wherever\
            \ the field holds a value."
        match:
          description: "What the values of the field have to satisfy, in the shape\
            \ the `match` of a `field` clause takes: a `range` for a bound, an `equals`\
            \ for a number written with its unit and nothing else. Can be sent back\
            \ as a filter as it is."
          $ref: "#/components/schemas/Matcher"
        words:
          type: array
          items:
            type: string
          description: "The words the filter was read from, as they were typed and\
            \ in the order they were typed."
        fallback:
          type: array
          items:
            $ref: "#/components/schemas/InterpretTarget"
          description: "The targets read instead where a document holds no value on\
            \ the field, in order, as the `fallback` of the target the request named.\
            \ Absent when there are none."
    FilterUsage:
      type: object
      description: Enables filtering search results by exact field value. Filtering
        is exact across all types; exact-match normalization for string fields is
        configured under `keyword`.
      examples:
      - {}
    FloatFieldDefinition:
      required:
      - type
      description: "Represents a 32-bit floating point number. Numeric fields do not\
        \ support text analysis and are searched by filtering, which supports exact\
        \ matches and range queries."
      examples:
      - type: float
        filter: {}
        sort: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - float
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
        signal:
          description: "Makes the field a ranking signal that is refreshed in place\
            \ through the document update action, without indexing the document again.\
            \ A signal field is sortable and is returned in results, but is left out\
            \ of the document source and cannot be combined with `filter`, `facet`,\
            \ `stored`, `multiple`, `locales` or `primaryKey`. A document indexed\
            \ without a value keeps the value the field holds. See [Signal fields](https://exofind.dev/reference/field-types/#signal-fields)."
          $ref: "#/components/schemas/SignalUsage"
        validation:
          description: Sets allowed numeric bounds. Documents containing values outside
            these bounds are rejected.
          $ref: "#/components/schemas/FloatValidation"
        unit:
          type: string
          description: "What the values are measured in: an ISO 4217 currency code\
            \ such as `SEK`, a CLDR unit identifier such as `kilogram` or `gigabyte`,\
            \ or any other text matched as written. A search in `user` mode reads\
            \ a number typed next to the unit, or next to a comparative word such\
            \ as `under`, as a filter on this field. Changing the unit does not require\
            \ a reindex. See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          examples:
          - kilogram
      type: object
    FloatValidation:
      type: object
      description: Sets the allowed numeric bounds for a `float` field. Documents
        containing values outside these bounds are rejected.
      examples:
      - min: 0
        max: 5
      properties:
        min:
          type: number
          format: float
          description: Lowest value accepted.
        max:
          type: number
          format: float
          description: Highest value accepted.
    Freshness:
      type: object
      description: "What the request demands of the state it is answered from. See\
        \ [Freshness](https://exofind.dev/reference/search-api/#freshness)."
      properties:
        atLeast:
          type: string
          description: "A freshness token that an earlier response returned in its\
            \ `freshness` property or `X-Exofind-Freshness` header. The node answers\
            \ only once it holds the state the token describes: the generation, the\
            \ commit and the search settings. A node that is behind commits, pulls\
            \ or reads the settings first, and answers `search:freshness:unavailable`\
            \ with a `Retry-After` header when it has waited `EXOFIND_SEARCH_FRESHNESS_WAIT`\
            \ without reaching the state. The token is opaque; pass it back unchanged."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    FuseClause:
      required:
      - type
      - rankings
      description: "Matches documents across several rankings, scored and merged by\
        \ rank. Documents are scored by the sum of `weight / (rankConstant + rank)`\
        \ across the rankings that reached them. Because the clause reads only result\
        \ positions, scores from different scales (such as BM25 text relevance and\
        \ vector similarity) combine without normalization. Matches at most `depth`\
        \ results per ranking. See [`fuse`](https://exofind.dev/reference/search-api/#fuse)."
      examples:
      - type: fuse
        depth: 200
        rankConstant: 60
        rankings:
        - clauses:
          - type: text
            text: waterproof jacket
            fields:
              name: null
        - clauses:
          - type: knn
            field: embedding
            vector:
            - 0.1
            - 0.2
            k: 200
          weight: 0.5
        filter:
        - field: inStock
          match:
            value: true
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - fuse
        rankings:
          type: array
          items:
            $ref: "#/components/schemas/FuseRanking"
          description: Rankings to run and merge. Specifying fewer than two rankings
            returns `search:clause:rankings_too_few`.
        depth:
          type: integer
          format: int32
          description: "Number of results read from each ranking. Pagination cannot\
            \ exceed the merged list, similar to `k` in a `knn` clause. Must be at\
            \ least `1`, and at most `EXOFIND_SEARCH_MAX_FUSE_DEPTH`."
          default: 100
        rankConstant:
          type: number
          format: float
          description: "Constant added to each rank before it is inverted. Lower values\
            \ increase the weight of the highest-ranked results in each ranking; higher\
            \ values flatten the difference across ranks, giving more weight to documents\
            \ found by multiple rankings. Must be above `0`."
          default: 60
        filter:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses that narrow every ranking before it is cut to `depth`.\
            \ A `knn` clause inside a ranking applies `filter` entries as a pre-filter,\
            \ ensuring the vector ranking returns `k` results. Clauses placed beside\
            \ the `fuse` clause filter the merged list after each ranking is cut to\
            \ `depth`."
      type: object
    FuseRanking:
      type: object
      description: "One ranking of a fusion: search clauses to evaluate and the ranking's\
        \ relative weight."
      examples:
      - clauses:
        - type: text
          text: waterproof jacket
          fields:
            name: null
        weight: 0.5
      required:
      - clauses
      properties:
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses the ranking searches for, combined with an implicit\
            \ `AND`. At least one clause is required."
        weight:
          type: number
          format: float
          description: Multiplier that scales the ranking's contribution relative
            to other rankings. It cannot reorder results within the ranking.
          default: 1
    GenerationSummary:
      description: One generation of an index.
      examples:
      - name: "2"
        live: true
        createdAt: 2026-08-16T11:02:07Z
      type: object
      properties:
        name:
          type: string
          description: "Name of the generation within its index, which is what follows\
            \ the `@` when addressing it as `products@2`."
          examples:
          - "2"
        live:
          type: boolean
          description: "Whether the index answers from this generation. Exactly one\
            \ generation of an index does, unless the index has just been created."
        createdAt:
          type: string
          description: "When the generation was created, as an ISO 8601 timestamp.\
            \ Omitted for a generation registered before this was recorded."
          examples:
          - 2026-08-16T11:02:07Z
    GeoPointFieldDefinition:
      required:
      - type
      description: "Represents a geographic location defined by WGS 84 `lat` and `lon`\
        \ coordinates. Locations are searched by distance rather than exact value:\
        \ `filter` enables the `distance` matcher, and `sort` enables ordering by\
        \ distance from an origin, nearest first."
      examples:
      - type: geo_point
        filter: {}
        sort: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - geo_point
        role:
          description: "Specifies a field role that applies a preset combination of\
            \ usages. The role expands into explicit field properties before the definition\
            \ is stored, and any property set alongside the role is preserved as given.\
            \ Supported roles per type are listed under [Field roles](https://exofind.dev/reference/field-types/#field-roles)."
          $ref: "#/components/schemas/Role"
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: Enables distance-based filtering with the `distance` matcher.
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: "Enables ordering documents by distance from a target origin,\
            \ nearest first."
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
      type: object
    Grant:
      type: object
      description: A stored grant combining permissions and index patterns.
      examples:
      - permissions:
        - indexes.read
        - search
        indexes:
        - products
      properties:
        permissions:
          type: array
          items:
            type: string
          description: "Permission names, in sorted order."
        indexes:
          type: array
          items:
            type: string
          description: Index names and prefix patterns in the order specified.
    GrantDefinition:
      type: object
      description: "A set of permissions over a set of index patterns. Every permission\
        \ in the grant applies to every matching index. A grant specifies `role`,\
        \ `permissions`, or both."
      examples:
      - role: reader
        indexes:
        - products
      properties:
        role:
          type: string
          description: "Shorthand for a set of permissions. `reader` grants `search`\
            \ and `indexes.read`. `writer` adds `documents.read`, `documents.write`,\
            \ `documents.delete`, and `indexes.commit`, but not `indexes.write`. `admin`\
            \ grants all permissions, including key management. When a key is created,\
            \ roles are expanded into their constituent permissions. Only the resulting\
            \ permissions are stored in the key. Existing keys do not change permissions\
            \ if role definitions change in later software versions."
          examples:
          - reader
          enum:
          - reader
          - writer
          - admin
        permissions:
          type: array
          items:
            type: string
          description: "Permissions by name, added to whatever `role` specifies. An\
            \ unknown permission name returns `auth:key:permission_unknown`."
        indexes:
          type: array
          items:
            type: string
          description: "Index names or prefix patterns the permissions apply to. An\
            \ index pattern is either an exact index name or a prefix followed by\
            \ `*`; a single `*` matches all indexes. Required for index-scoped permissions\
            \ and ignored for deployment-scoped permissions. Generations are named\
            \ `index@generation`: `products` matches the index but no generation of\
            \ it, `products@*` matches every generation but not the index itself,\
            \ and `products*` matches both."
    Hierarchy:
      type: object
      description: Enables path hierarchy matching.
      examples:
      - separator: /
      properties:
        separator:
          type: string
          description: Specifies the string that separates hierarchy levels. Changing
            the separator on an index that contains documents requires reindexing
            them.
          default: /
    Highlight:
      type: object
      description: "Requests highlighted snippets. Fragments are generated only from\
        \ scoring clauses, so non-scoring filter clauses produce no highlights. Highlighted\
        \ text is not HTML-escaped, and text beyond the first 10,000 characters of\
        \ a field value is not evaluated. See [Highlighting](https://exofind.dev/reference/search-api/#highlighting)."
      examples:
      - fields:
          name: {}
          description:
            fragments: 2
      required:
      - fields
      properties:
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/HighlightField"
          description: "Fields to return fragments for, keyed by the name the field\
            \ has in the index definition. An empty options object asks for the defaults.\
            \ Fields must have highlighting enabled (`matching` or `autocomplete`);\
            \ requesting an unconfigured field returns `search:usage_unsupported`."
    HighlightField:
      type: object
      description: Configuration for highlighting text fragments in a single field.
      examples:
      - fragments: 2
        length: 150
        pre: <mark>
        post: </mark>
      properties:
        fragments:
          type: integer
          format: int32
          description: Maximum number of fragments to return.
          default: 3
        length:
          type: integer
          format: int32
          maximum: 10000
          minimum: 1
          description: "Target character length per fragment. Fragments break on sentence\
            \ boundaries, and text shorter than this comes back as a single fragment\
            \ holding all of it."
          default: 150
        pre:
          type: string
          description: Prefix tag inserted before highlighted terms. May be empty.
          default: <em>
        post:
          type: string
          description: Postfix tag inserted after highlighted terms. May be empty.
          default: </em>
    HighlightUsage:
      type: object
      description: Enables highlighted snippet extraction within matching text. Contains
        no configuration properties.
      examples:
      - {}
    Hit:
      type: object
      description: "One result: usually a document that matched, or - for a search\
        \ whose `hits` names an object field - one matched value of that field, with\
        \ `index` and `value` present and the document holding it under `document`."
      examples:
      - id: "9781234567890"
        score: 8.42
        document:
          name: Silent Spring
          price: 12.5
        highlights:
          name:
          - Silent <em>Spring</em>
      properties:
        id:
          description: "Primary key of the document, omitted on an index that has\
            \ none. A value hit carries the key of the document holding it, so several\
            \ hits share an `id` when several values of one document matched - the\
            \ identity of such a hit is `id` together with `key`, or with `index`\
            \ on a field that declares no key."
          examples:
          - 9781234567890
        index:
          type: integer
          format: int32
          description: "Zero-based position of the value this hit stands for in the\
            \ parent document's value array. Present only when the search asked for\
            \ value hits. A reindex is free to reorder values, so this names the same\
            \ value only for as long as the document is not written again - `key`\
            \ is what does not move."
        key:
          type: string
          description: "What the value this hit stands for reads for the `key` its\
            \ object field declares. Two values of one document never read the same,\
            \ so `id` and `key` name one value and go on naming it after a reindex.\
            \ Omitted for a document hit, for a field that declares no key, and on\
            \ an index whose `source` is `none` when the key's field is not `stored`."
        score:
          type: number
          format: float
          description: "How well the hit matched. Omitted when the search computed\
            \ no scores, rather than defaulted to something that looks like a value:\
            \ a search of plain filters carrying no ranking signal and no `rescore`\
            \ is ordered by nothing a score could say. A value hit scores what its\
            \ document scored plus what the value itself scored under the `nested`\
            \ clauses of its path."
          examples:
          - 8.42
        value:
          description: "The matched nested value object, keyed by field name. Present\
            \ only when the search requests value hits. On an index whose `source`\
            \ is `none` it holds the value's `stored` fields, and is omitted when\
            \ nothing of the value is stored."
          type: object
        document:
          description: "Selected fields of the document per the search request `fields`\
            \ property, keyed by field name and shaped as indexed. A field declared\
            \ `multiple` is an array, and a locale-specific field is an object holding\
            \ the single variant read for the query locale. For a value hit, returns\
            \ the fields of the parent document."
          type: object
        highlights:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: "Highlighted fragments keyed by field name. Present when highlighting\
            \ is requested, omitting fields with no matching text in the hit - for\
            \ a value hit the fragments are cut from the hit's own value. Omitted\
            \ when highlighting is not requested."
        matched:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/MatchedValues"
          description: "Matched nested values keyed by field name, with one entry\
            \ per requested field. Omitted when matched values are not requested."
    Hits:
      type: object
      description: "Makes each matched value of a `nested` object field a hit of its\
        \ own instead of a document hit. Totals count matching nested values, facets\
        \ count value hits, and pagination cursors step through values. With `when`,\
        \ only the documents it matches expand and the rest stay document hits, totals\
        \ count hits of both kinds and facets count documents. Cannot be combined\
        \ with `matched` (`search:hits:matched_unsupported`) or `knn` clauses (`search:hits:knn_unsupported`);\
        \ `highlight` may only name fields inside `path` (`search:hits:highlight_field_not_inside`),\
        \ and each hit returns fragments of its own value. See [What a hit stands\
        \ for](https://exofind.dev/reference/search-api/#what-a-hit-stands-for)."
      examples:
      - path: variants
        fields:
        - variants.color
        - variants.price
      required:
      - path
      properties:
        path:
          type: string
          description: Dotted path of the nested object field whose matched values
            become hits. Targeting a field that is not a `nested` object returns `search:hits:path_not_nested`.
          examples:
          - variants
        fields:
          type: array
          items:
            type: string
          description: "Dotted field paths inside the nested object to return in `value`,\
            \ defaulting to all of them. Names must be prefixed by `path` (`search:hits:field_not_inside`)\
            \ and exist in the index (`search:field_unknown`). On an index whose `source`\
            \ is `none`, a named field has to be `stored` (`search:usage_unsupported`)."
        when:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses deciding which documents expand into value hits; every\
            \ other matching document stays a document hit. Combined with an implicit\
            \ `AND`, and specified as `field` or `nested` clauses. If omitted, every\
            \ matching document expands. Unsupported clause types return `search:hits:when_clause_unsupported`;\
            \ clauses that score return `search:hits:when_scoring_unsupported`. Sorting\
            \ by a field is refused while this is set (`search:hits:when_sort_unsupported`)."
    Hnsw:
      type: object
      description: Hierarchical Navigable Small World index configuration. Parameters
        trade indexing time and space for recall; omitting them uses engine defaults.
      examples:
      - m: 16
        efConstruction: 100
      properties:
        m:
          type: integer
          format: int32
          description: Number of bi-directional links per node.
        efConstruction:
          type: integer
          format: int32
          description: Size of dynamic candidate list evaluated during index construction.
    HtmlStrip:
      type: object
      description: Strips HTML and XML markup and keeps text between tags. Carries
        no options.
      examples:
      - {}
    IcuTokenizer:
      type: object
      description: Unicode segmentation. Carries no options.
      examples:
      - {}
    InMatcher:
      required:
      - type
      - values
      description: Matches field values equal to any value in `values`. An empty array
        matches no documents.
      examples:
      - type: in
        values:
        - fiction
        - poetry
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - in
        values:
          type: array
          items: {}
          description: The values that a field value may equal.
      type: object
    IndexDefinition:
      description: "What an index contains and how it can be searched. A definition\
        \ is the state a caller wants: it is sent in full and anything left out is\
        \ removed. Observed state, such as whether the index is usable, is reported\
        \ separately under `status`."
      examples:
      - fields:
          id:
            type: string
            primaryKey: true
            required: true
          name:
            type: string
            matching: {}
            sort: {}
          category:
            type: string
            filter: {}
            facet: {}
          price:
            type: float
            filter: {}
            sort: {}
          published:
            type: boolean
            filter: {}
      type: object
      properties:
        source:
          description: "How much of a document the index retains. `full` retains the\
            \ entire document, enabling full document retrieval and reindexing from\
            \ the index itself. `none` retains only fields configured as `stored`.\
            \ Changing this setting applies to documents indexed after the update."
          default: full
          $ref: "#/components/schemas/Source"
        metadata:
          type: object
          additionalProperties:
            type: string
          description: "Free-form metadata for the index, not interpreted by the engine."
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/FieldDefinition"
          description: "The fields of the index, keyed by field name. A name contains\
            \ letters, numbers, underscores, and `*`, which defines multiple fields\
            \ at once; a name never contains a dot, which addresses fields inside\
            \ `object` fields. The wildcard matches exactly one name. Explicit definitions\
            \ take precedence, and the longest literal prefix wins among multiple\
            \ wildcard patterns."
        ranking:
          description: "Tie-breaking rules and ranking signals. Omitted to order results\
            \ by match score alone. Replaced entirely while an index has [search settings](https://exofind.dev/reference/admin-api/#search-settings)."
          $ref: "#/components/schemas/Ranking"
        resources:
          description: "Shared resources referenced by name from fields, including\
            \ named analysis chains, stopword lists, and synonym sets."
          $ref: "#/components/schemas/Resources"
        locales:
          description: "Declares the locales that localized fields in the index support.\
            \ A field opts in with `\"locales\": {}` to hold all declared locales,\
            \ or narrows to fewer with `\"locales\": { \"only\": [...] }`. The engine\
            \ expands the declaration onto each field before storing the index definition,\
            \ so reading a definition back returns the locales on each field. If omitted,\
            \ each field declares its own locales."
          $ref: "#/components/schemas/IndexLocales"
        localeFallback:
          description: "Configures how missing locale values in a document are populated\
            \ from available locales during indexing. When omitted, missing locales\
            \ remain empty, and searches in a locale find only documents translated\
            \ into it."
          $ref: "#/components/schemas/LocaleFallback"
    IndexInfo:
      description: "An index together with the definition and status of one of its\
        \ generations: the generation specified in the request, or the live generation\
        \ if omitted. See [Index resource](https://exofind.dev/reference/admin-api/#index-resource)."
      examples:
      - name: products
        generation: "2"
        live: true
        version: 9f2c1a0b3d4e5f60
        definition:
          fields:
            id:
              type: string
              primaryKey: true
              required: true
            name:
              type: string
              matching: {}
              sort: {}
        status:
          state: usable
          readOnly: false
          indexer:
            node: node-a-7f21
            address: http://node-a:8080
          luceneCompatibility: current
        generations:
        - name: "1"
          live: false
          createdAt: 2026-08-16T11:02:07Z
        - name: "2"
          live: true
          createdAt: 2026-08-28T10:15:30Z
      type: object
      properties:
        name:
          type: string
          description: The name of the index.
          examples:
          - products
        generation:
          type: string
          description: "The generation described in the response. When the request\
            \ specifies only the index name, this is the live generation."
          examples:
          - "2"
        live:
          type: boolean
          description: A boolean indicating whether this generation is the live generation.
        version:
          type: string
          description: "An identifier for the definition, also returned in the `ETag`\
            \ header. Pass this value in the `If-Match` header on `PUT` requests to\
            \ prevent overwriting concurrent updates."
          examples:
          - 9f2c1a0b3d4e5f60
        definition:
          description: The active index definition. Presets are stored expanded; the
            response returns the expanded chain rather than the preset name.
          $ref: "#/components/schemas/IndexDefinition"
        status:
          description: The observed state reported by the answering node. The API
            does not accept this object as input.
          $ref: "#/components/schemas/IndexStatus"
        generations:
          type: array
          items:
            $ref: "#/components/schemas/GenerationSummary"
          description: "A list of all generations for the index, ordered by name."
        freshness:
          type: string
          description: "A freshness token for the state the action landed in. Present\
            \ on the answer to a commit and to a promotion, and omitted elsewhere.\
            \ Pass it as `freshness.atLeast` on a search, and the search is answered\
            \ only once the node holds the commit or answers from the promoted generation.\
            \ Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    IndexListResponse:
      description: The indexes held across the deployment. Definitions and status
        are omitted; request a specific index to retrieve them.
      examples:
      - indexes:
        - name: products
          liveGeneration: "2"
          generations:
          - name: "1"
            live: false
            createdAt: 2026-08-16T11:02:07Z
          - name: "2"
            live: true
            createdAt: 2026-08-28T10:15:30Z
      type: object
      properties:
        indexes:
          type: array
          items:
            $ref: "#/components/schemas/IndexSummary"
          description: "The indexes visible to the key, ordered by name."
        next:
          type: string
          description: The name to pass as the `after` parameter to list the indexes
            after these. Present only when a `limit` cut the listing short.
          examples:
          - products
    IndexLocales:
      type: object
      description: "Declares the locales that localized fields in an index support,\
        \ defined once at the index level instead of on every field. The engine expands\
        \ these locales onto each field before storing the index definition."
      examples:
      - defaultLocale: en
        supported:
        - sv
        - de
      required:
      - defaultLocale
      properties:
        defaultLocale:
          type: string
          description: Specifies the BCP-47 locale for values that carry no explicit
            locale. A field uses this value as its default locale unless the field
            defines its own `defaultLocale`. This property is required whenever an
            index declares `locales`.
          examples:
          - en
        supported:
          type: array
          items:
            type: string
          description: "Lists additional BCP-47 locales that the index supports, in\
            \ addition to `defaultLocale`. A field can narrow to a subset of these\
            \ supported locales and the default locale by using `only`."
    IndexState:
      type: string
      enum:
      - needs_pull
      - usable
      - modified
      - pulling
      - pushing
      - unsupported
      - incompatible
      - closed
    IndexStatus:
      type: object
      description: "The observed state reported by the answering node. The API does\
        \ not accept this object as input. See [Index states](https://exofind.dev/reference/admin-api/#index-states)."
      examples:
      - state: usable
        readOnly: false
        indexer:
          node: node-a-7f21
          address: http://node-a:8080
        luceneCompatibility: current
        luceneCreatedMajor: 10
      properties:
        state:
          description: "The remote synchronization state as observed by the answering\
            \ node: `needs_pull` (a newer remote state exists and has not been pulled\
            \ yet), `pulling` (the node is fetching remote state), `usable` (the index\
            \ is serving searches), `modified` (the index has local changes that are\
            \ not yet pushed; only writer nodes reach this state), `pushing` (the\
            \ node is pushing local changes), `unsupported` (the definition requires\
            \ engine features not present on this node version), `incompatible` (the\
            \ Lucene files are too old for this build to open), or `closed` (the index\
            \ is closed on this node; a new request opens a fresh instance)."
          $ref: "#/components/schemas/IndexState"
        readOnly:
          type: boolean
          description: Indicates whether the answering node can modify the index.
            Only the node holding the index can modify it; other nodes serve searches
            from their local copy.
        indexer:
          description: "Identifies the holder node and the address where writes are\
            \ forwarded. Omitted if no node holds the index, if the holder could not\
            \ be read, if the holder provided no address, or on nodes using local\
            \ storage where `readOnly` already answers. Data in this field can lag\
            \ behind a node handover by a few seconds."
          $ref: "#/components/schemas/IndexerInfo"
        luceneCompatibility:
          description: "Indicates Lucene version compatibility. `current`: created\
            \ by the current major version, and compatible with the current and next\
            \ Lucene major versions. `ending`: readable by the current version, but\
            \ unsupported by the next Lucene major version; reindex before upgrading\
            \ across major versions. `unreadable`: too old to open; the index reports\
            \ the `incompatible` state and requires reindexing. `unknown`: no version\
            \ was recorded and no commit exists to determine the version, such as\
            \ on an empty index."
          $ref: "#/components/schemas/LuceneCompatibility"
        luceneCreatedMajor:
          type: integer
          format: int32
          description: The recorded Lucene major version the index was created with.
            Omitted when compatibility is `unknown`.
          examples:
          - 10
        settingsUnsupportedFeatures:
          type: array
          items:
            type: string
          description: Lists the capabilities the index's search settings use that
            the answering node does not have. Present only when the node has set the
            settings aside and searches with the definition alone; upgrading the node
            puts them back in force.
    IndexSummary:
      type: object
      description: An index and the generations it holds.
      examples:
      - name: products
        liveGeneration: "2"
        generations:
        - name: "1"
          live: false
        - name: "2"
          live: true
      properties:
        name:
          type: string
          description: The name of the index.
          examples:
          - products
        liveGeneration:
          type: string
          description: The generation the index answers from. Omitted when no generation
            is live.
          examples:
          - "2"
        generations:
          type: array
          items:
            $ref: "#/components/schemas/GenerationSummary"
          description: "Every generation of the index, ordered by name."
    IndexerInfo:
      type: object
      description: "The node currently writing an index, read from shared deployment\
        \ state. Reported by the engine and never accepted as input."
      examples:
      - node: node-a-7f21
        address: http://node-a:8080
      properties:
        node:
          type: string
          description: The name the node competes under.
          examples:
          - node-a-7f21
        address:
          type: string
          description: The target address for write forwarding. Omitted when the node
            did not set `EXOFIND_NODE_ADDRESS`.
          examples:
          - http://node-a:8080
    IndexerListResponse:
      description: "Candidate nodes competing to write indexes and the active writer\
        \ claim for each index. The response reflects the answering node's view of\
        \ shared deployment state and can lag actual state by a few seconds. On nodes\
        \ using local storage, both lists are empty."
      examples:
      - candidates:
        - node: node-a-7f21
          address: http://node-a:8080
          expiresAt: 2026-08-21T10:15:30Z
        claims:
        - index: products
          node: node-a-7f21
          address: http://node-a:8080
          expiresAt: 2026-08-21T10:15:30Z
      type: object
      properties:
        candidates:
          type: array
          items:
            $ref: "#/components/schemas/Candidate"
          description: "The candidate nodes competing to write indexes, ordered by\
            \ node."
        claims:
          type: array
          items:
            $ref: "#/components/schemas/Claim"
          description: "The active writer claim for each index, ordered by index.\
            \ Indexes without an active claim are omitted until a write assigns a\
            \ writer. Claims on indexes where the key lacks permissions are also omitted."
    Int32FieldDefinition:
      required:
      - type
      description: "Represents a 32-bit signed integer. Numeric fields do not support\
        \ text analysis and are searched by filtering, which supports exact matches\
        \ and range queries."
      examples:
      - type: int32
        filter: {}
        validation:
          min: 0
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - int32
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
        signal:
          description: "Makes the field a ranking signal that is refreshed in place\
            \ through the document update action, without indexing the document again.\
            \ A signal field is sortable and is returned in results, but is left out\
            \ of the document source and cannot be combined with `filter`, `facet`,\
            \ `stored`, `multiple`, `locales` or `primaryKey`. A document indexed\
            \ without a value keeps the value the field holds. See [Signal fields](https://exofind.dev/reference/field-types/#signal-fields)."
          $ref: "#/components/schemas/SignalUsage"
        validation:
          description: Sets allowed numeric bounds. Documents containing values outside
            these bounds are rejected.
          $ref: "#/components/schemas/Int32Validation"
        unit:
          type: string
          description: "What the values are measured in: an ISO 4217 currency code\
            \ such as `SEK`, a CLDR unit identifier such as `kilogram` or `gigabyte`,\
            \ or any other text matched as written. A search in `user` mode reads\
            \ a number typed next to the unit, or next to a comparative word such\
            \ as `under`, as a filter on this field. Changing the unit does not require\
            \ a reindex. See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          examples:
          - SEK
      type: object
    Int32Validation:
      type: object
      description: Sets the allowed numeric bounds for an `int32` field. Documents
        containing values outside these bounds are rejected.
      examples:
      - min: 0
        max: 100
      properties:
        min:
          type: integer
          format: int32
          description: Lowest value accepted.
          examples:
          - 0
        max:
          type: integer
          format: int32
          description: Highest value accepted.
    Int64FieldDefinition:
      required:
      - type
      description: "Represents a 64-bit signed integer. Numeric fields do not support\
        \ text analysis and are searched by filtering, which supports exact matches\
        \ and range queries."
      examples:
      - type: int64
        filter: {}
        sort: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - int64
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
        signal:
          description: "Makes the field a ranking signal that is refreshed in place\
            \ through the document update action, without indexing the document again.\
            \ A signal field is sortable and is returned in results, but is left out\
            \ of the document source and cannot be combined with `filter`, `facet`,\
            \ `stored`, `multiple`, `locales` or `primaryKey`. A document indexed\
            \ without a value keeps the value the field holds. See [Signal fields](https://exofind.dev/reference/field-types/#signal-fields)."
          $ref: "#/components/schemas/SignalUsage"
        validation:
          description: Sets allowed numeric bounds. Documents containing values outside
            these bounds are rejected.
          $ref: "#/components/schemas/Int64Validation"
        unit:
          type: string
          description: "What the values are measured in: an ISO 4217 currency code\
            \ such as `SEK`, a CLDR unit identifier such as `kilogram` or `gigabyte`,\
            \ or any other text matched as written. A search in `user` mode reads\
            \ a number typed next to the unit, or next to a comparative word such\
            \ as `under`, as a filter on this field. Changing the unit does not require\
            \ a reindex. See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          examples:
          - gigabyte
      type: object
    Int64Validation:
      type: object
      description: Sets the allowed numeric bounds for an `int64` field. Documents
        containing values outside these bounds are rejected.
      examples:
      - min: 0
        max: 1099511627776
      properties:
        min:
          type: integer
          format: int64
          description: Lowest value accepted.
        max:
          type: integer
          format: int64
          description: Highest value accepted.
    Interpret:
      oneOf:
      - $ref: "#/components/schemas/InterpretMode"
      - $ref: "#/components/schemas/InterpretTargets"
      description: "Whether parts of `user` text are read as filters on the fields\
        \ of the index. A string selects a mode: `auto` reads a number typed next\
        \ to the unit of a number field, or next to a comparative word such as `under`,\
        \ as a filter on that field; `off` takes every word as text. An object with\
        \ `fields` reads the same way but only on the targets it names, for an index\
        \ where several fields hold the same unit and the caller knows which one is\
        \ meant. Whatever was read is reported as `interpreted` beside the results.\
        \ See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
    InterpretMode:
      description: "Whether parts of `user` text are read as filters: `auto` reads\
        \ a number typed next to a unit or a comparative word as a filter on the field\
        \ declaring that unit, `off` takes every word as text."
      type: string
      enum:
      - auto
      - "off"
    InterpretTarget:
      type: object
      description: "A field a reading may be a filter on. The field must be a number\
        \ field declaring a `unit`; naming one without returns `search:interpret:unit_required`.\
        \ A field inside a `nested` [object field](https://exofind.dev/reference/field-types/#object)\
        \ is named by its dotted path and read against one value at a time, with `when`\
        \ saying which."
      examples:
      - field: prices.amount
        when:
        - field: prices.list
          match:
            value: customer
        fallback:
        - field: prices.amount
          when:
          - field: prices.list
            match:
              value: store
      required:
      - field
      properties:
        field:
          type: string
          description: "The field, as named in the index definition."
          examples:
          - prices.amount
        when:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses that must hold where the number is read: in the same\
            \ value as the field for a field inside a `nested` list, and for the document\
            \ otherwise. Takes what a `nested` clause takes: `field`, `text`, `and`,\
            \ `or`, `not` and `boost`. A clause naming a field outside the list returns\
            \ `search:nested:field_not_inside`."
        fallback:
          type: array
          items:
            $ref: "#/components/schemas/InterpretTarget"
          description: "Targets read instead, in order, where the document holds no\
            \ value on this one - a product with no price on the customer's list is\
            \ read on the store's list. Every target of the chain must declare the\
            \ same unit; one in another unit returns `search:interpret:fallback_unit_mismatch`."
    InterpretTargets:
      description: "Reads `user` text as filters on the named targets only. A number\
        \ typed with a unit is read on every target declaring that unit; a number\
        \ typed without one is read on every target holding a currency, when they\
        \ all hold the same currency."
      examples:
      - fields:
        - field: prices.amount
      type: object
      required:
      - fields
      properties:
        fields:
          type: array
          items:
            $ref: "#/components/schemas/InterpretTarget"
          description: The targets a reading may be a filter on. At least one is required.
    InterpretUsage:
      type: object
      description: Reads the values of the field out of the query text. Carries no
        configuration options.
      examples:
      - {}
    Interpreted:
      type: object
      description: "The filters a search read out of the query text, and the text\
        \ that was left once their words were taken out. Present only when something\
        \ was read. The words of a filter are still searched as text, so the results\
        \ hold what the filter finds as well as what the words find as text, with\
        \ the filter ranked first."
      examples:
      - filters:
        - field: price
          match:
            type: range
            lt: 500
          words:
          - under
          - "500"
        text: shoes
      properties:
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Filter"
          description: "The filters that were read, in the order their words were\
            \ typed. Two fields declaring the same unit read the same words as two\
            \ filters, either of which a document may satisfy."
        text:
          type: string
          description: The text that was left once the words of the filters were taken
            out. Empty when everything typed was read.
          examples:
          - shoes
    Join:
      description: "How the parts of `user` text combine: `all` requires every word\
        \ and every quoted phrase, `any` accepts a document holding one of them. Excluded\
        \ terms always apply."
      type: string
      enum:
      - all
      - any
    KeyDefinition:
      description: "What a key should be allowed to do. See [Permissions](https://exofind.dev/reference/auth/#permissions)."
      examples:
      - description: the search backend
        grants:
        - role: reader
          indexes:
          - products
        expiresAt: 2027-01-01T00:00:00Z
      type: object
      properties:
        description:
          type: string
          description: "What the key is for, so whoever lists the keys later can tell\
            \ them apart. Read by nobody but a human."
          examples:
          - the search backend
        grants:
          type: array
          items:
            $ref: "#/components/schemas/GrantDefinition"
          description: "What the key may do, evaluated as a union: a request is allowed\
            \ if any grant permits it, and there are no deny rules. At least one grant\
            \ is required, as a key with none could do nothing."
        expiresAt:
          type: string
          description: "An ISO 8601 timestamp string defining when the key expires.\
            \ If omitted, the key does not expire."
          examples:
          - 2027-01-01T00:00:00Z
      required:
      - grants
    KeyInfo:
      type: object
      description: A key as stored by the deployment. Key secrets are stored only
        as hashes. A lost credential cannot be recovered and must be replaced.
      examples:
      - id: 4ff6b760264c1918
        description: the search backend
        grants:
        - permissions:
          - indexes.read
          - search
          indexes:
          - products
        createdAt: 2026-08-16T12:09:33.198275Z
        expiresAt: 2027-01-01T00:00:00Z
      properties:
        id:
          type: string
          description: "Key identifier. Server logs record the key ID, never the credential\
            \ value."
          examples:
          - 4ff6b760264c1918
        description:
          type: string
          description: A string describing the key.
          examples:
          - the search backend
        grants:
          type: array
          items:
            $ref: "#/components/schemas/Grant"
          description: "Grants assigned to the key, with roles expanded into their\
            \ constituent permissions."
        createdAt:
          type: string
          description: An ISO 8601 timestamp string defining when the key was created.
          examples:
          - 2026-08-16T12:09:33.198275Z
        expiresAt:
          type: string
          description: An ISO 8601 timestamp string defining when the key expires.
            `null` for a key that does not expire.
          examples:
          - 2027-01-01T00:00:00Z
    KeyListResponse:
      description: Deployment API keys and node key configuration. The keys are shared
        across all nodes. The remaining fields reflect the local configuration of
        the node answering the request.
      examples:
      - keys:
        - id: 4ff6b760264c1918
          description: the search backend
          grants:
          - permissions:
            - indexes.read
            - search
            indexes:
            - products
          createdAt: 2026-08-16T12:09:33.198275Z
          expiresAt: null
        rootKeyConfigured: true
        anonymousKey: null
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: "#/components/schemas/KeyInfo"
          description: "Deployment keys shared across all nodes, ordered by ID."
        rootKeyConfigured:
          type: boolean
          description: Whether this node has a root key configured with `EXOFIND_AUTH_ROOT_KEY`.
            The root key is not stored in key storage and cannot be listed or revoked
            through the API.
        anonymousKey:
          type: string
          description: "ID of the key used for unauthenticated requests, configured\
            \ with `EXOFIND_AUTH_ANONYMOUS_KEY`. `null` when the node rejects unauthenticated\
            \ requests. An anonymous key cannot contain any permission other than\
            \ `search`."
          examples:
          - fe3747c2761ef89d
        next:
          type: string
          description: The ID to pass as the `after` parameter to list the keys after
            these. Present only when a `limit` cut the listing short.
          examples:
          - 4ff6b760264c1918
    Keyword:
      type: object
      description: Configures exact-match normalization for filtering.
      examples:
      - caseFolding: true
      properties:
        caseFolding:
          type: boolean
          description: "When `true`, folds case before values are compared, allowing\
            \ filters on `Fiction` to match `fiction`."
          default: true
    KeywordTokenizer:
      type: object
      description: Retains the entire input value as a single token. Carries no options.
      examples:
      - {}
    KnnClause:
      required:
      - type
      - field
      - vector
      - k
      description: "Matches the `k` nearest documents by vector distance in a specified\
        \ field, scored by proximity. Cannot be combined with `hits` (`search:hits:knn_unsupported`)."
      examples:
      - type: knn
        field: embedding
        vector:
        - 0.1
        - 0.2
        k: 10
        filter:
        - field: published
          match:
            value: true
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - knn
        field:
          type: string
          description: The vector field to search.
          examples:
          - embedding
        vector:
          type: array
          items:
            type: number
            format: float
          description: The query vector. Its length must match the dimensions declared
            in the field definition.
        k:
          type: integer
          format: int32
          description: "Number of nearest documents to return, at most `EXOFIND_SEARCH_MAX_KNN_K`."
        filter:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: Clauses that documents must satisfy before nearest-neighbor
            evaluation.
      type: object
    LengthNormalization:
      description: "Controls the field length penalty in ranking: `none` applies no\
        \ penalty, `moderate` applies standard prose normalization, and `strong` applies\
        \ the full penalty for short fields such as titles."
      type: string
      enum:
      - none
      - moderate
      - strong
    LetterTokenizer:
      type: object
      description: Splits text on non-letter characters. Carries no options.
      examples:
      - {}
    Linear:
      type: object
      description: "Ranks a value as `value / ceiling`, held between `0` and `1`.\
        \ The shape for a score computed elsewhere that already lies in a known range."
      examples:
      - ceiling: 1
      required:
      - ceiling
      properties:
        ceiling:
          type: number
          format: double
          exclusiveMinimum: 0
          description: The value that counts for all of what the signal can give.
            Must be above zero.
          examples:
          - 1
    LocaleFallback:
      type: object
      description: "Fills missing locale values in a document from available translations\
        \ during indexing. Fallback values are analyzed using the target fallback\
        \ locale. Document retrieval is unaffected: documents return as originally\
        \ provided. Applies to every locale-specific field except those setting `\"\
        locales\": { \"fallback\": \"disabled\" }`. Modifying fallback rules applies\
        \ only to documents indexed after the change."
      examples:
      - chain:
        - da
        - en
      properties:
        chain:
          type: array
          items:
            type: string
          description: "Ordered list of locales to evaluate when populating a missing\
            \ locale value. A field skips locales for which it has no value, allowing\
            \ a single chain to serve fields with different configured locales. Specifying\
            \ a locale not defined on any field in the index is rejected. When omitted,\
            \ each field falls back to its `defaultLocale`."
    Locales:
      type: object
      description: "Configures locale-specific field values. On an index that declares\
        \ `locales`, configuring `{}` gives the field every declared locale, and `only`\
        \ narrows the field to a subset of those locales. See [Localize fields](https://exofind.dev/how-to/localize-fields/)."
      examples:
      - defaultLocale: sv
        locales:
        - en
        - de
      properties:
        defaultLocale:
          type: string
          description: "Specifies the BCP-47 fallback locale for values that carry\
            \ no explicit locale. On an index that declares `locales`, this property\
            \ defaults to the `defaultLocale` of the index."
          examples:
          - sv
        locales:
          type: array
          items:
            type: string
          description: "Lists the supported locales for the field, in addition to\
            \ the default locale. The engine rejects documents containing values with\
            \ unlisted locales, and queries can target any listed locale. The engine\
            \ rejects this property on an index that declares `locales`, which narrows\
            \ a field with `only` instead. Reading an index definition back always\
            \ returns this list on each field."
        only:
          type: array
          items:
            type: string
          description: "Specifies the locales this field holds from the locales declared\
            \ in index-level `locales`. If omitted, the field holds all declared locales.\
            \ Every tag must be one the index declares, and the list must contain\
            \ the default locale of the field. The engine expands this property into\
            \ `defaultLocale` and `locales` before storing the index definition, so\
            \ reading a definition back returns those properties instead."
        fallback:
          description: Controls whether this field participates in the index's `localeFallback`.
            Only evaluated on an index that declares a fallback; setting `enabled`
            on an index without fallback configuration is rejected.
          default: enabled
          $ref: "#/components/schemas/Fallback"
    LuceneCompatibility:
      type: string
      enum:
      - unknown
      - current
      - ending
      - unreadable
    Mapping:
      type: object
      description: The input and target terms of a one-way synonym mapping rule.
      examples:
      - from:
        - ny
        to:
        - new york
      required:
      - from
      - to
      properties:
        from:
          type: array
          items:
            type: string
          description: Source terms matched by the mapping rule.
        to:
          type: array
          items:
            type: string
          description: Target terms that the source terms map to.
    MappingCharFilter:
      type: object
      description: Literal text replacements applied before tokenization.
      examples:
      - mappings:
          '&': ' and '
      required:
      - mappings
      properties:
        mappings:
          type: object
          additionalProperties:
            type: string
          description: Replaces occurrences of each key with its value.
    Match:
      description: "Term matching mode: `all` requires every term, `any` requires\
        \ one, `phrase` requires them in order and adjacent, and `user` parses search\
        \ syntax such as quotes and negation."
      type: string
      enum:
      - all
      - any
      - phrase
      - user
    Matched:
      type: object
      description: "Requests the matched values of `nested` object fields with each\
        \ hit. Cannot be combined with `hits` (`search:hits:matched_unsupported`).\
        \ See [Matched values](https://exofind.dev/reference/search-api/#matched-values)."
      examples:
      - fields:
          variants:
            limit: 3
      required:
      - fields
      properties:
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/MatchedField"
          description: "Object fields to answer for, keyed by the name the field has\
            \ in the index definition. An empty options object asks for the defaults.\
            \ Targeting a field that is not a `nested` object returns `search:matched:field_not_nested`."
    MatchedField:
      type: object
      description: Configuration for returning matched values of a nested object field.
      examples:
      - limit: 3
        fields:
        - variants.color
      properties:
        limit:
          type: integer
          format: int32
          maximum: 100
          minimum: 1
          description: Maximum number of matched values to return per hit. How many
            matched in all always comes back beside them.
          default: 3
        fields:
          type: array
          items:
            type: string
          description: "Field paths inside the nested object to include in each returned\
            \ value, defaulting to all of them. Paths must reside under the target\
            \ object path (`search:matched:field_not_inside`) and exist in the schema\
            \ (`search:field_unknown`). On an index whose `source` is `none`, a named\
            \ field has to be `stored` (`search:usage_unsupported`)."
    MatchedValues:
      type: object
      description: Matched values of a nested object field for a hit.
      examples:
      - values:
        - color: red
          price: 18.0
        - color: blue
          price: 19.5
        totalValues: 5
      properties:
        values:
          description: "Array of matched nested values, each keyed by field name,\
            \ up to `limit`. If scoring clauses exist within the `nested` clause,\
            \ values are ordered by score; otherwise, they appear in document order.\
            \ On an index whose `source` is `none` each value holds its `stored` fields,\
            \ and the array is omitted when nothing of the values is stored."
          type: array
          items: {}
        totalValues:
          type: integer
          format: int32
          description: Total count of matched values for the nested field in the document.
            Exceeds the number of entries under `values` when the limit is reached.
    Matcher:
      oneOf:
      - $ref: "#/components/schemas/EqualsMatcher"
      - $ref: "#/components/schemas/InMatcher"
      - $ref: "#/components/schemas/AnyMatcher"
      - $ref: "#/components/schemas/PrefixMatcher"
      - $ref: "#/components/schemas/UnderMatcher"
      - $ref: "#/components/schemas/RangeMatcher"
      - $ref: "#/components/schemas/RangesMatcher"
      - $ref: "#/components/schemas/TextMatcher"
      - $ref: "#/components/schemas/DistanceMatcher"
      description: "Criteria evaluated against field values in a field clause, structured\
        \ as a tagged union where `type` selects the matcher type. The engine also\
        \ accepts a matcher that omits `type`, which it reads as an `equals` matcher.\
        \ Specifying a matcher unsupported by the target field type returns an error.\
        \ See [Matchers](https://exofind.dev/reference/search-api/#matchers)."
      examples:
      - value: fiction
      discriminator:
        propertyName: type
        mapping:
          equals: "#/components/schemas/EqualsMatcher"
          in: "#/components/schemas/InMatcher"
          any: "#/components/schemas/AnyMatcher"
          prefix: "#/components/schemas/PrefixMatcher"
          under: "#/components/schemas/UnderMatcher"
          range: "#/components/schemas/RangeMatcher"
          ranges: "#/components/schemas/RangesMatcher"
          text: "#/components/schemas/TextMatcher"
          distance: "#/components/schemas/DistanceMatcher"
    MatcherRange:
      type: object
      description: "One range of a `ranges` matcher, bounded on each side by an inclusive\
        \ or exclusive bound. At least one bound is required."
      examples:
      - gte: 10
        lt: 20
      properties:
        gte:
          description: "Lower bound, the value itself included."
          examples:
          - 10
        gt:
          description: "Lower bound, the value itself excluded."
        lte:
          description: "Upper bound, the value itself included."
        lt:
          description: "Upper bound, exclusive."
          examples:
          - 20
    Missing:
      description: "Placement of documents without values when sorting in ascending\
        \ order: `first` or `last`."
      type: string
      enum:
      - first
      - last
    Mode:
      description: "Storage mode for multiple objects. `nested` retains each object\
        \ instance as an isolated sub-document for use with the `nested` clause, matched\
        \ values, and value hits. `flattened` indexes child fields directly into the\
        \ parent document structure under their dot-notation paths, and object boundaries\
        \ are not preserved."
      type: string
      enum:
      - nested
      - flattened
    NestedClause:
      required:
      - type
      - path
      description: "Matches documents where a single element of a `nested` [object\
        \ field](https://exofind.dev/reference/field-types/#object) satisfies all\
        \ child clauses. A `nested` clause on a flattened object field returns `search:nested:path_not_nested`;\
        \ on a non-object field it returns an error. See [`nested`](https://exofind.dev/reference/search-api/#nested)."
      examples:
      - type: nested
        path: variants
        clauses:
        - field: variants.color
          match:
            value: red
        - field: variants.price
          match:
            type: range
            lt: 20
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - nested
        path:
          type: string
          description: Name of the nested object field.
          examples:
          - variants
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses evaluated within a single nested object value, naming\
            \ fields by their dotted path. An empty array matches any document where\
            \ the object field is present. May contain `field`, `text`, `knn`, `and`,\
            \ `or`, `not` and `boost`; a clause that only means something for whole\
            \ documents, such as another `nested` or a `fuse`, returns `search:nested:clause_unsupported`."
        score:
          description: Scoring mode for aggregating matching nested values. Only applies
            when scoring clauses exist within the nested clause.
          default: max
          $ref: "#/components/schemas/Score"
      type: object
    Ngram:
      type: object
      description: Generates substring n-grams for tokens within the specified character
        lengths.
      examples:
      - minGram: 3
        maxGram: 5
      properties:
        minGram:
          type: integer
          format: int32
          description: The shortest substring to index.
        maxGram:
          type: integer
          format: int32
          description: The longest substring to index.
    Normalize:
      type: object
      description: Unicode normalization and case folding.
      examples:
      - caseFolding: true
      properties:
        caseFolding:
          type: boolean
          description: Whether case folding is applied.
          default: true
    NotClause:
      required:
      - type
      - clauses
      description: Matches documents where no child clause matches.
      examples:
      - type: not
        clauses:
        - field: discontinued
          match:
            value: true
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - not
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Child clauses, none of which may match."
      type: object
    Numbers:
      type: object
      description: Enables typo tolerance for digit-only words. Contains no configuration
        properties.
      examples:
      - {}
    ObjectFieldDefinition:
      required:
      - type
      description: "Represents structured object values containing nested field definitions,\
        \ referenced by dot notation (such as `variants.price`). An object field cannot\
        \ configure `filter`, `sort`, `facet`, `locales`, or `stored` on itself. Child\
        \ fields can be objects in turn, though a `nested` array cannot contain another\
        \ `nested` array. An array of objects can specify a `key` to identify each\
        \ object value. Object fields are returned in search results through the preserved\
        \ document source; a stored child field below single objects also answers\
        \ when the index keeps none. See [`object`](https://exofind.dev/reference/field-types/#object)."
      examples:
      - type: object
        multiple: true
        mode: nested
        key: sku
        fields:
          sku:
            type: string
            required: true
            filter: {}
          color:
            type: string
            filter: {}
          price:
            type: double
            filter: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - object
        primaryKey:
          type: boolean
          description: Not supported on an object field; setting it is rejected.
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field holds a list of object values, and\
            \ `mode` is required. Single object fields are always indexed as flattened\
            \ objects."
          default: false
        stored:
          type: boolean
          description: Not supported on an object field; setting it is rejected.
        locales:
          description: Not supported on an object field; setting it is rejected.
          $ref: "#/components/schemas/Locales"
        filter:
          description: Not supported on an object field; setting it is rejected.
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Not supported on an object field; setting it is rejected.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: Not supported on an object field; setting it is rejected.
          $ref: "#/components/schemas/FacetUsage"
        mode:
          description: Storage mode for multiple objects. Required when `multiple`
            is `true` (`index:field:object:mode_required`) and rejected when it is
            not (`index:field:object:mode_without_multiple`).
          $ref: "#/components/schemas/Mode"
        key:
          type: string
          description: "Names a child field as the unique identifier for each object\
            \ value in an array. Targets object values in update paths (such as `variants[V-2]`)\
            \ and populates `key` on search value hits. Requires `multiple: true`\
            \ (`index:field:object:key_without_multiple`). Must name a field defined\
            \ in `fields` (`index:field:object:key_unknown`) that is `required`, not\
            \ `multiple`, and of type `string`, `int32`, or `int64` (`index:field:object:key_invalid`).\
            \ Duplicate key values within a document are rejected with `document:object_key_duplicate`."
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/FieldDefinition"
          description: "Map of child field names to field definitions, the `object`\
            \ type included - objects nest, though a `nested` array cannot contain\
            \ another `nested` array (`index:field:object:nested_in_nested`). What\
            \ a child field may configure follows from where it sits: `sort` and `stored`\
            \ are rejected below a `flattened` array (`index:field:object:flattened_sort_unsupported`,\
            \ `index:field:object:flattened_stored_unsupported`), and `primaryKey`\
            \ is rejected inside any object (`index:field:object:inner_usage_unsupported`).\
            \ Below a `nested` array `stored` and `highlight` work - highlighted fragments\
            \ come back on value hits. `locales` works everywhere."
      type: object
    OrClause:
      required:
      - type
      - clauses
      description: Matches documents where at least one child clause matches.
      examples:
      - type: or
        clauses:
        - field: category
          match:
            value: fiction
        - field: category
          match:
            value: poetry
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - or
        clauses:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Child clauses, at least one of which must match."
      type: object
    Page:
      type: object
      description: Pagination state and navigation cursors for the result window.
      examples:
      - limit: 20
        offset: 0
        next: AWtaPJHiAAAS1QFmQErhSA
      properties:
        limit:
          type: integer
          format: int32
          description: Maximum number of results returned in the page window.
          examples:
          - 20
        offset:
          type: integer
          format: int32
          description: Number of matching results skipped before the window. Omitted
            when navigating with `next` or `previous` cursors. Cursors encode positions
            rather than count offsets and are not restricted by `EXOFIND_SEARCH_MAX_PAGE_DEPTH`.
        previous:
          type: string
          description: "Cursor for the preceding window, passed in `before`. Omitted\
            \ on the first window of a search paged by `offset`. A window reached\
            \ by a cursor carries it whenever the window is full, which says there\
            \ may be results before it rather than that there are, so following it\
            \ can answer an empty window."
        next:
          type: string
          description: "Cursor for the next window, passed in `after`. Omitted on\
            \ the final window of a search paged by `offset`. A window reached by\
            \ a cursor carries it whenever the window is full, which says there may\
            \ be results after it rather than that there are, so following it can\
            \ answer an empty window. A cursor is an opaque token. Pass it back as\
            \ it arrived, and do not read anything out of it."
        pages:
          description: "Numbered page metadata, present when requested."
          $ref: "#/components/schemas/PagesResult"
    PageRef:
      type: object
      description: Metadata for a single numbered page.
      examples:
      - number: 3
        cursor: AW9aPJHiAAAAKA
        current: true
      properties:
        number:
          type: integer
          format: int64
          description: 1-based page number.
          examples:
          - 3
        cursor:
          type: string
          description: "Cursor that fetches the page, passed in `after`."
        current:
          type: boolean
          description: True for the current page; omitted on all other pages.
    PagesRequest:
      type: object
      description: "Requests numbered page metadata. Sending an empty object asks\
        \ for the defaults. Can be combined with `offset` or page cursors, but not\
        \ with `after` or `before`."
      examples:
      - max: 9
      properties:
        max:
          type: integer
          format: int32
          description: Maximum number of page entries to return.
          default: 9
    PagesResult:
      type: object
      description: "Numbered page metadata, divided into `start`, `middle`, and `end`\
        \ arrays to render `1 2 3 … 7` with ellipses at window boundaries. Page numbers\
        \ are 1-based. Cursors inside encode count offsets and remain subject to `EXOFIND_SEARCH_MAX_PAGE_DEPTH`."
      examples:
      - count: 7
        next:
          number: 4
          cursor: AW9aPJHiAAAAPA
        start:
        - number: 1
          cursor: AW9aPJHiAAAAAA
        - number: 2
          cursor: AW9aPJHiAAAAFA
        - number: 3
          cursor: AW9aPJHiAAAAKA
          current: true
        end:
        - number: 7
          cursor: AW9aPJHiAAAAeA
      properties:
        count:
          type: integer
          format: int64
          description: Total number of pages.
          examples:
          - 7
        previous:
          description: Metadata for the page preceding the current page. Omitted on
            the first page.
          $ref: "#/components/schemas/PageRef"
        next:
          description: Metadata for the page following the current page. Omitted on
            the final page and when the page exceeds maximum page depth.
          $ref: "#/components/schemas/PageRef"
        start:
          type: array
          items:
            $ref: "#/components/schemas/PageRef"
          description: Page entries at the start of the list.
        middle:
          type: array
          items:
            $ref: "#/components/schemas/PageRef"
          description: "Page entries surrounding the current page, present when they\
            \ touch neither end of the list."
        end:
          type: array
          items:
            $ref: "#/components/schemas/PageRef"
          description: Page entries at the end of the list. Omitted when the final
            page exceeds maximum page depth.
    PatternReplace:
      type: object
      description: Replaces substrings that match a regular expression.
      examples:
      - pattern: \s+
        replacement: ' '
      required:
      - pattern
      - replacement
      properties:
        pattern:
          type: string
          description: The regular expression to match.
        replacement:
          type: string
          description: What each match is replaced with.
    Prefix:
      description: "Prefix matching behavior on the final query term: `last_token`\
        \ matches the trailing word as a prefix, `off` requires an exact word match."
      type: string
      enum:
      - last_token
      - "off"
    PrefixMatcher:
      required:
      - type
      - value
      description: "Matches string field values starting with `value`, evaluated against\
        \ the entire field value."
      examples:
      - type: prefix
        value: EX-
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - prefix
        value:
          type: string
          description: The prefix that a field value must start with.
          examples:
          - EX-
      type: object
    Preset:
      description: "A predefined analyzer chain. `preserve_terms` tokenizes and normalizes\
        \ text, but keeps each word whole, for names, codes, and SKUs. `full_text`\
        \ tokenizes and normalizes text, removes stopwords, splits compound words,\
        \ and stems words, for prose."
      type: string
      enum:
      - preserve_terms
      - full_text
    Quantization:
      description: "Vector compression method: `none`, `int8`, or `int4`."
      type: string
      enum:
      - none
      - int8
      - int4
    QuerySynonyms:
      type: object
      description: "A synonym set applied to the search query at query time, rather\
        \ than to document values during indexing."
      examples:
      - rules:
        - equivalent:
          - laptop
          - notebook
        fields:
        - name
        boost: 0.8
      properties:
        rules:
          type: array
          items:
            $ref: "#/components/schemas/Rule"
          description: "The rules of the set, using the same shape as rules in an\
            \ index definition's `resources`."
        fields:
          type: array
          items:
            type: string
          description: "An optional list of field names the set applies to, named\
            \ as a search names them. If omitted, the set applies to every field searched\
            \ as text. Target fields are validated against the generation the index\
            \ answers from at write time; a generation promoted later that lacks a\
            \ named field causes searches to skip that field rather than fail."
        boost:
          type: number
          format: float
          description: A positive number specifying what a term added by the rules
            counts against the typed term. Default `0.8`. Values below `1` rank a
            document holding the typed term above one holding only a synonym. A value
            of `1` weighs synonyms and typed terms equally.
          examples:
          - 0.8
    RangeMatcher:
      required:
      - type
      description: "Matches values within bounds. Accepts inclusive (`gte`, `lte`)\
        \ and exclusive (`gt`, `lt`) bounds; either side may be left open, and at\
        \ least one bound is required (`search:matcher:range_empty`)."
      examples:
      - type: range
        gte: 10
        lt: 20
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - range
        gte:
          description: "Lower bound, the value itself included."
          examples:
          - 10
        gt:
          description: "Lower bound, the value itself excluded."
        lte:
          description: "Upper bound, the value itself included."
        lt:
          description: "Upper bound, exclusive."
          examples:
          - 20
      type: object
    RangesMatcher:
      required:
      - type
      - values
      description: "Matches values falling within any of the specified range objects.\
        \ An empty array matches no documents, matching the behavior of an empty `in`\
        \ matcher."
      examples:
      - type: ranges
        values:
        - gte: 10
          lt: 20
        - gte: 50
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - ranges
        values:
          type: array
          items:
            $ref: "#/components/schemas/MatcherRange"
          description: "The ranges to evaluate, each requiring at least one bound.\
            \ A bucket returned by a range facet sets `from` as `gte` and `to` as\
            \ `lt`."
      type: object
    Ranking:
      type: object
      description: "Tie-breaking rules and signal score multipliers. See [Relevance](https://exofind.dev/explanation/relevance/)."
      examples:
      - tieBreakers:
        - field: sales
          direction: descending
        signals:
        - field: purchases
          saturation:
            pivot: 50
          weight: 0.5
      properties:
        tieBreakers:
          type: array
          items:
            $ref: "#/components/schemas/TieBreaker"
          description: Secondary sort criteria applied in sequence after primary sort
            or relevance scoring until ties are resolved. Target fields must have
            `sort` enabled.
        signals:
          type: array
          items:
            $ref: "#/components/schemas/SignalDefinition"
          description: "Document values multiplied into relevance scores in sequence.\
            \ Evaluated at search time without reindexing, and applied only when results\
            \ are ordered by relevance. A search request that specifies `signals`\
            \ adds to these rules or replaces them based on `signalsMode`."
    RankingDecay:
      type: object
      description: Halves the multiplier every `halfLife` seconds of age. Values dated
        at or after the current time evaluate to `1`.
      examples:
      - halfLife: 604800
      required:
      - halfLife
      properties:
        halfLife:
          type: integer
          format: int64
          exclusiveMinimum: 0
          description: "How many seconds it takes for the signal to be worth half\
            \ as much. Required, and must be greater than `0`."
          examples:
          - 604800
    RankingLinear:
      type: object
      description: "Computes `value / ceiling`, held between `0` and `1`. Values below\
        \ `0` evaluate to `0` and values above the ceiling to `1`. The shape for a\
        \ score computed elsewhere that already lies in a known range, such as an\
        \ engagement score between `0` and `1`."
      examples:
      - ceiling: 1
      required:
      - ceiling
      properties:
        ceiling:
          type: number
          format: double
          exclusiveMinimum: 0
          description: "The value that counts for all of what the signal can give.\
            \ Required, and must be greater than `0`."
          examples:
          - 1
    RankingSaturation:
      type: object
      description: "Computes `value / (value + pivot)`, reaching `0.5` at the pivot\
        \ and approaching but never reaching `1` above it. Values below `0` evaluate\
        \ to `0`. The shape for a count with no ceiling, such as how often something\
        \ was bought."
      examples:
      - pivot: 50
      required:
      - pivot
      properties:
        pivot:
          type: number
          format: double
          exclusiveMinimum: 0
          description: "The value that counts for half of what the signal can give.\
            \ Required, and must be greater than `0`."
          examples:
          - 50
    Reason:
      description: "Reason a word was dropped: `unmatched` when the word does not\
        \ exist in the index; `common` when it is one of the most common words across\
        \ documents."
      type: string
      enum:
      - unmatched
      - common
    Registry:
      type: string
      enum:
      - present
      - absent
      - corrupt
    RegistryAuditResponse:
      description: "The registry compared with what remote storage holds. Reported\
        \ by the engine and never accepted as input. See [Audit](https://exofind.dev/reference/admin-api/#audit)."
      examples:
      - registry: present
        indexes:
        - name: products
          registered: true
          live: "2"
          generations:
          - name: "1"
            registered: true
            stored: synced
          - name: "2"
            registered: true
            stored: synced
        - name: staging
          registered: false
          removedAt: 2026-09-03T10:15:00Z
          generations:
          - name: "1"
            registered: false
            stored: synced
        unusable: []
      type: object
      properties:
        registry:
          description: "The state of the registry object: `present`, `absent` (no\
            \ registry object), or `corrupt` (contents cannot be parsed)."
          $ref: "#/components/schemas/Registry"
        indexes:
          type: array
          items:
            $ref: "#/components/schemas/AuditedIndex"
          description: "Every index named by the registry or found in storage, ordered\
            \ by name."
        unusable:
          type: array
          items:
            type: string
          description: Storage prefixes whose names no index or generation may carry
            (as `index` or `index/generation`). A repair never registers these prefixes.
    RegistryRepairRequest:
      description: "How a repair should treat the indexes it creates, and which deleted\
        \ indexes or generations it should bring back. The entire body is optional."
      examples:
      - promoteNewest: true
        restore:
        - books
      type: object
      properties:
        promoteNewest:
          type: boolean
          description: "When `true`, each index created by the repair answers for\
            \ its highest-numbered generation. Hand-named generations are not selected.\
            \ Indexes that are already registered keep what they answer for. When\
            \ `false`, a created index answers for nothing until a generation is promoted."
          default: false
        restore:
          type: array
          items:
            type: string
          description: "Names of deleted indexes (`books`) or generations (`books@2`)\
            \ whose storage the sweep has not removed yet, to bring back. The repair\
            \ registers what each one holds like any other unregistered storage, then\
            \ takes the removal mark off it. A name whose storage holds no `synced`\
            \ generation keeps its mark, so the sweep still removes it. A name without\
            \ a mark changes nothing. Deleted storage is never registered without\
            \ being named here."
          examples:
          - - books
    RegistryRepairResponse:
      description: "Summary of the changes made by a repair. Every list empty means\
        \ the registry already named everything storage holds, nothing was restored,\
        \ and nothing was written."
      examples:
      - createdIndexes:
        - products
        addedGenerations:
        - products@2
        promoted:
        - products@2
        restored:
        - products
      type: object
      properties:
        createdIndexes:
          type: array
          items:
            type: string
          description: "Index entries added to the registry, ordered by name."
        addedGenerations:
          type: array
          items:
            type: string
          description: "Generations added to the registry, formatted as `index@generation`\
            \ and ordered by name."
        promoted:
          type: array
          items:
            type: string
          description: "Generations made live by the repair, formatted as `index@generation`\
            \ and ordered by name."
        restored:
          type: array
          items:
            type: string
          description: "Deleted indexes and generations whose removal mark the repair\
            \ took off, as `index` or `index@generation`, in the order the request\
            \ named them. What they hold appears in the other lists."
    ReindexInfo:
      description: "A reindex job record. See [Job record and phases](https://exofind.dev/reference/admin-api/#job-record-and-phases)."
      examples:
      - id: 6f1c2a9d8b3e4c05
        index: products
        target: products@2
        source: products@1
        phase: copying
        promote: auto
        documentsCopied: 125000
        sourceDocuments: 2400000
        backlog: 4100
        error: null
        startedBy: 3f9a1c7e2b8d4650
        node: node-a-7f21
        startedAt: 2026-08-28T10:15:30Z
        updatedAt: 2026-08-28T10:16:02Z
        finishedAt: null
        freshness: null
      type: object
      properties:
        id:
          type: string
          description: "The id of the job, minted when it was accepted. Tells one\
            \ job of an index from the one that replaced it. `null` on a record an\
            \ earlier version wrote."
          examples:
          - 6f1c2a9d8b3e4c05
        index:
          type: string
          description: The name of the index.
          examples:
          - products
        target:
          type: string
          description: "The generation being populated, formatted as `index@generation`."
          examples:
          - products@2
        source:
          type: string
          description: The generation providing the source documents.
          examples:
          - products@1
        phase:
          type: string
          description: "The current phase of the job. `pending`: accepted and waiting\
            \ for a concurrency slot on the node. `copying`: streaming documents from\
            \ the source to the target in primary key order. `replaying`: copying\
            \ documents that changed in the source while the copy ran. `ready`: used\
            \ only with `promote: manual`, caught up and waiting for manual promotion,\
            \ while continuing to catch up periodically. `promoting`: holding writes\
            \ for the final drain and promotion. `done`: completed and promoted successfully.\
            \ `failed`: stopped before promotion due to an error, indicated by `error`.\
            \ `cancelled`: stopped before completion in response to a cancellation\
            \ request."
          examples:
          - copying
          enum:
          - pending
          - copying
          - replaying
          - ready
          - promoting
          - done
          - failed
          - cancelled
        promote:
          type: string
          description: The configured promote mode. `auto` automatically promotes
            the target generation once it catches up with changes. `manual` pauses
            the job in the `ready` phase and keeps the target caught up until you
            manually promote it.
          examples:
          - auto
          enum:
          - auto
          - manual
        documentsCopied:
          type: integer
          format: int64
          description: The number of confirmed documents copied to the target.
          examples:
          - 125000
        sourceDocuments:
          type: integer
          format: int64
          description: The document count of the source generation when the copy started.
          examples:
          - 2400000
        backlog:
          type: integer
          format: int64
          description: The number of changed documents waiting to be replayed when
            the record was last written.
          examples:
          - 4100
        error:
          type: string
          description: "The error message if the job failed, or `null`."
        startedBy:
          type: string
          description: "The id of the principal whose request started the job: the\
            \ id of a key, or the name of a configured principal such as `root`. `null`\
            \ on a record an earlier version wrote."
          examples:
          - 3f9a1c7e2b8d4650
        node:
          type: string
          description: "The name of the node running the job, as the indexer listing\
            \ names it, or the node that ended it. A job resumed on another node after\
            \ a failover names that node from its next checkpoint on. `null` on a\
            \ record an earlier version wrote."
          examples:
          - node-a-7f21
        startedAt:
          type: string
          description: The timestamp when the job started.
          examples:
          - 2026-08-28T10:15:30Z
        updatedAt:
          type: string
          description: The timestamp when the job record was last updated.
          examples:
          - 2026-08-28T10:16:02Z
        finishedAt:
          type: string
          description: "The timestamp when the job reached `done`, `failed` or `cancelled`,\
            \ or `null` while it runs."
          examples:
          - 2026-08-28T10:41:17Z
        freshness:
          type: string
          description: "A freshness token for the state the promotion of the target\
            \ landed in. Present once the job reaches `done`, and `null` before that.\
            \ Pass it as `freshness.atLeast` on a search, and the search is answered\
            \ from the target generation whichever node it lands on. Opaque; pass\
            \ it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATI
    ReindexListResponse:
      description: "Every reindex job across the deployment, finished ones included."
      examples:
      - reindexes:
        - id: 6f1c2a9d8b3e4c05
          index: products
          target: products@2
          source: products@1
          phase: copying
          promote: auto
          documentsCopied: 125000
          sourceDocuments: 2400000
          backlog: 4100
          error: null
          startedBy: 3f9a1c7e2b8d4650
          node: node-a-7f21
          startedAt: 2026-08-28T10:15:30Z
          updatedAt: 2026-08-28T10:16:02Z
          finishedAt: null
      type: object
      properties:
        reindexes:
          type: array
          items:
            $ref: "#/components/schemas/ReindexInfo"
          description: "The jobs the key can view, ordered by index name. A job on\
            \ an index on which the key lacks permissions is omitted."
        next:
          type: string
          description: The index name to pass as the `after` parameter to list the
            jobs after these. Present only when a `limit` cut the listing short.
          examples:
          - products
    ReindexRequest:
      description: Configuration for a reindex job. Both fields are optional; an empty
        body reads from the live generation and promotes automatically.
      examples:
      - from: products@1
        promote: manual
      type: object
      properties:
        from:
          type: string
          description: "The generation to read documents from: an index name, or a\
            \ generation such as `products@1`. Must belong to the same index as the\
            \ target. Defaults to the live generation."
          examples:
          - products@1
        promote:
          type: string
          description: The promotion mode. `auto` (default) automatically promotes
            the target generation once it catches up with changes. `manual` pauses
            the job in the `ready` phase and keeps the target caught up until you
            manually promote it.
          default: auto
          enum:
          - auto
          - manual
    Relax:
      description: "Query relaxation strategy: `unmatched` drops words that do not\
        \ exist in the index, `words` also drops the most common remaining words one\
        \ by one until results are found, and `off` returns an empty result set. Whatever\
        \ was dropped is reported as `relaxed` beside the results."
      type: string
      enum:
      - "off"
      - unmatched
      - words
    Relaxed:
      type: object
      description: Details of dropped query terms when query relaxation was applied.
        Present only when the initial query produced zero results. Total counts and
        facet counts reflect the relaxed search.
      examples:
      - dropped:
        - word: waterproof
          reason: unmatched
        text: running shoes
      properties:
        dropped:
          type: array
          items:
            $ref: "#/components/schemas/Dropped"
          description: "List of dropped words and the reason each was removed, in\
            \ the order they appeared in the query."
        text:
          type: string
          description: The effective query string used to execute the search.
          examples:
          - running shoes
    Rescore:
      description: "Reorders the best results of a search in a second pass without\
        \ changing which documents matched. Boosts and signals apply only inside the\
        \ window, reordering relevant results without promoting non-matching documents.\
        \ Applies only when results are ordered by relevance; providing an explicit\
        \ `sort` overrides rescoring. See [Rescoring](https://exofind.dev/reference/search-api/#rescoring)."
      examples:
      - window: 200
        boost:
        - field: brand
          match:
            value: adidas
        signals:
        - field: purchases
          saturation:
            pivot: 50
        weight: 0.5
      type: object
      required:
      - window
      properties:
        window:
          type: integer
          format: int32
          description: "Number of best results to score a second time. Must be at\
            \ least `offset` plus `limit`, and at most `EXOFIND_SEARCH_MAX_RESCORE_WINDOW`."
          examples:
          - 200
        boost:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: Clauses that lift results satisfying them. Clauses do not filter
            or narrow search hits; a result that satisfies none of them keeps its
            first-pass score. Wrap a clause in `boost` to weigh it against the others.
        signals:
          type: array
          items:
            $ref: "#/components/schemas/Signal"
          description: "Document values taken into the second score, written the same\
            \ way as top-level `signals`. Applied to every result in the window. The\
            \ ranking configured on the index belongs to the first pass and is not\
            \ applied again here, so `signalsMode` does not affect these."
        weight:
          type: number
          format: float
          description: Multiplier applied to the second-pass score before adding it
            to the first-pass score.
          default: 1
    Resources:
      type: object
      description: "Shared resources defined for an index, referenced by name from\
        \ individual fields."
      examples:
      - analyzers:
          prose:
            preset: full_text
        stopwords:
          brands:
          - acme
        synonyms:
          cars:
            rules:
            - equivalent:
              - car
              - automobile
            - mapping:
                from:
                - ny
                to:
                - new york
      properties:
        analyzers:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/AnalyzerDefinition"
          description: "Named analysis chains, referenced from a field usage with\
            \ `\"analyzer\": { \"named\": \"...\" }`. Presets are expanded the same\
            \ way as on a field."
        stopwords:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: "Named stopword lists, referenced from the stopwords component\
            \ of an analyzer chain with `\"stopwords\": { \"named\": \"...\" }`."
        synonyms:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/Synonyms"
          description: "Named synonym sets, referenced from the synonyms component\
            \ of an analyzer chain with `\"synonyms\": { \"named\": \"...\" }`."
    Role:
      description: "Defines a preset combination of usages for a common kind of field.\
        \ The engine expands a role into explicit field properties before storing\
        \ the index definition, so reading the definition back returns the individual\
        \ usages rather than the role name. Each role applies only to the field types\
        \ that support it."
      type: string
      enum:
      - id
      - title
      - description
      - tag
      - path
      - code
      - timestamp
      - geo
    Rule:
      type: object
      description: "A single synonym rule, configured as either equivalent terms or\
        \ a one-way mapping."
      examples:
      - equivalent:
        - car
        - automobile
      properties:
        equivalent:
          type: array
          items:
            type: string
          description: Interchangeable terms where each term matches every other term.
            Multi-word terms match words in sequence.
        mapping:
          description: "A one-way mapping rule: values containing a term in `from`\
            \ also match searches for any term in `to`, but not the reverse."
          $ref: "#/components/schemas/Mapping"
    Saturation:
      type: object
      description: "Ranks a value as `value / (value + pivot)` - half at the pivot,\
        \ approaching but never reaching one above it."
      examples:
      - pivot: 50
      required:
      - pivot
      properties:
        pivot:
          type: number
          format: double
          exclusiveMinimum: 0
          description: The value that counts for half of what the signal can give.
            Must be above zero.
          examples:
          - 50
    ScanResponse:
      description: "A batch of documents read from an index. A single request reads\
        \ from a point-in-time snapshot and sees committed data only, so uncommitted\
        \ writes are not visible. For more information, see [Reading documents](https://exofind.dev/reference/documents-api/#reading-documents)."
      examples:
      - documents:
        - id: "1"
          name:
            sv: blåbärssylt
          energy: 234
        - id: "2"
          name:
            sv: hallonsylt
          energy: 241
        next: "2"
        freshness: AQoIcHJvZHVjdHMSATIYBw
      type: object
      properties:
        documents:
          description: "Documents in primary key order, formatted as originally indexed.\
            \ Whole-number keys return in numeric order, with negative numbers first.\
            \ Text keys return in UTF-8 byte order."
          type: array
          items: {}
        next:
          type: string
          description: "Primary key to pass as the `after` parameter on the next request.\
            \ Present only when the response returns as many documents as requested\
            \ by `limit`. If a batch ends exactly on the last document of the index,\
            \ `next` is returned and the subsequent request returns an empty `documents`\
            \ array without a `next` field."
          examples:
          - "2"
        freshness:
          type: string
          description: "A freshness token for the state the documents were read from.\
            \ Pass it in the `X-Exofind-Freshness` header of the next request, and\
            \ that request is answered from this state or a later one whichever node\
            \ it lands on. Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    Score:
      description: "How the matching nested values of a document combine into its\
        \ score: `max`, `min`, `avg` or `total`."
      type: string
      enum:
      - max
      - min
      - avg
      - total
    ScoreSort:
      required:
      - type
      description: Sorts by document relevance score.
      examples:
      - type: score
      properties:
        type:
          description: Selects the sort type.
          type: string
          enum:
          - score
        order:
          description: Direction to order in.
          default: desc
          $ref: "#/components/schemas/SortOrder"
      type: object
    SearchRequest:
      description: All request properties are optional. An empty request matches all
        documents in the index.
      examples:
      - query:
        - type: text
          text: silent spr
          fields:
            name: 3
        - field: published
          match:
            value: true
        filters:
        - field: category
          match:
            type: in
            values:
            - fiction
            - poetry
        facets:
        - field: category
        sort:
        - type: score
        - field: name
          order: asc
        fields:
        - name
        - price
        limit: 20
      type: object
      properties:
        query:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Clauses that a matching document must satisfy. Clauses in\
            \ the array are combined with an implicit `AND`. Evaluated clauses narrow\
            \ all facet counts. If omitted, matches all documents."
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Refinement clauses, specified as `field` clauses or `nested`\
            \ clauses. Filters narrow hits, but facets on the filtered field exclude\
            \ their own filter entries from counts by default (see [Facets](https://exofind.dev/reference/search-api/#facets)).\
            \ Unsupported clause types return `search:filter:clause_unsupported`.\
            \ Clauses that score results return `search:filter:scoring_unsupported`."
        facets:
          type: array
          items:
            $ref: "#/components/schemas/FacetRequest"
          description: "Fields to aggregate match counts for. See [Facets](https://exofind.dev/reference/search-api/#facets).\
            \ If omitted, no facet counts are calculated."
        sort:
          type: array
          items:
            $ref: "#/components/schemas/Sort"
          description: "Order in which results are returned. If omitted, results are\
            \ sorted by relevance score in descending order."
        locale:
          type: string
          description: "BCP-47 locale tag used to read and return locale-specific\
            \ fields. Matches the closest declared locale on each field (for example,\
            \ `sv-SE` falls back to `sv`). If no matching variant exists, uses the\
            \ field default."
          examples:
          - sv
        fields:
          type: array
          items:
            type: string
          description: "Document fields to return with each result. Fields inside\
            \ an [`object`](https://exofind.dev/reference/field-types/#object) are\
            \ specified by dotted path and returned nested inside the object. Requesting\
            \ unretrievable fields returns an error (see [Document source](https://exofind.dev/reference/field-types/#document-source)).\
            \ The primary key is always included."
        highlight:
          description: "Fields to return highlighted snippets for. See [Highlighting](https://exofind.dev/reference/search-api/#highlighting)."
          $ref: "#/components/schemas/Highlight"
        matched:
          description: "Nested object fields for which to return matched values with\
            \ each hit. See [Matched values](https://exofind.dev/reference/search-api/#matched-values)."
          $ref: "#/components/schemas/Matched"
        hits:
          description: "Specifies an object field whose matched values return as individual\
            \ hits instead of full documents. See [What a hit stands for](https://exofind.dev/reference/search-api/#what-a-hit-stands-for)."
          $ref: "#/components/schemas/Hits"
        limit:
          type: integer
          format: int32
          description: "Maximum number of results to return, at most `EXOFIND_SEARCH_MAX_LIMIT`.\
            \ Setting `limit` to `0` returns the total match count without hits."
          default: 10
        offset:
          type: integer
          format: int32
          description: "Number of matching results to skip. Specify at most one of\
            \ `offset`, `after`, or `before`."
          default: 0
        after:
          type: string
          description: Cursor string from the `next` property of a previous response
            to fetch the next page.
        before:
          type: string
          description: Cursor string from the `previous` property of a previous response
            to fetch the preceding page.
        pages:
          description: "Requests numbered page metadata. Accepts an optional `{ \"\
            max\": n }` object to limit the number of page entries (default `9`).\
            \ Implies `\"total\": \"exact\"`."
          $ref: "#/components/schemas/PagesRequest"
        total:
          default: estimate
          $ref: "#/components/schemas/TotalMode"
        signals:
          type: array
          items:
            $ref: "#/components/schemas/Signal"
          description: "Document ranking signals used to adjust relevance scoring.\
            \ Added to the signals configured on the index unless `signalsMode` says\
            \ otherwise. See [Signals](https://exofind.dev/reference/search-api/#signals).\
            \ If omitted, uses the ranking signals configured on the index."
        signalsMode:
          description: "How `signals` meets the ranking configured on the index: `\"\
            add\"` ranks by both, with a signal here standing in for one on the same\
            \ field; `\"replace\"` ranks by `signals` alone. Supplying this without\
            \ `signals` returns `search:signal:mode_without_signals`."
          default: add
          $ref: "#/components/schemas/SignalsMode"
        rescore:
          description: "Reorders the best results of a search in a second pass without\
            \ changing which documents matched. See [Rescoring](https://exofind.dev/reference/search-api/#rescoring)."
          $ref: "#/components/schemas/Rescore"
        freshness:
          description: "What the request demands of the state it is answered from.\
            \ Omit it to be answered from what the node holds. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          $ref: "#/components/schemas/Freshness"
    SearchResponse:
      description: "The results of a search. See [Response](https://exofind.dev/reference/search-api/#response)."
      examples:
      - hits:
        - id: "9781234567890"
          score: 8.42
          document:
            name: Silent Spring
            price: 12.5
        - id: "9780007458424"
          score: 3.17
          document:
            name: Spring Snow
            price: 9.95
        total:
          count: 128
          exact: true
        facets:
          category:
            values:
            - value: fiction
              count: 87
            - value: poetry
              count: 41
            totalValues: 2
        page:
          limit: 20
          offset: 0
          next: AWtaPJHiAAAS1QFmQErhSA
        generation: "2"
        tookMs: 7.412
      type: object
      properties:
        hits:
          type: array
          items:
            $ref: "#/components/schemas/Hit"
          description: "The matching hits, in the order that `sort` asked for."
        total:
          description: "How many hits matched in total, counted in whatever the search\
            \ answers with: a document that expanded into values counts once per value."
          $ref: "#/components/schemas/Total"
        documents:
          description: "How many documents matched, which is what the facets are counted\
            \ in. Present only for a search whose `hits` names a `when`, where some\
            \ documents expand into values and the rest do not; omitted otherwise,\
            \ where it would be the same number as `total`. A document that `when`\
            \ expands with no matching value under `path` counts here while answering\
            \ with no hit."
          $ref: "#/components/schemas/Total"
        facets:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/FacetResult"
          description: Facet results keyed by facet name. Omitted entirely when the
            request asked for no facets.
        page:
          description: "Where in the results this window sits, and how to move from\
            \ it."
          $ref: "#/components/schemas/Page"
        relaxed:
          description: "What the search let go of to find anything. Omitted entirely\
            \ when the query was not relaxed, so its presence always means the results\
            \ answer less than what was asked for."
          $ref: "#/components/schemas/Relaxed"
        interpreted:
          description: "What the search read out of the query text as filters, and\
            \ the text that was left. Omitted entirely when nothing was read. See\
            \ [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          $ref: "#/components/schemas/Interpreted"
        generation:
          type: string
          description: "Name of the generation that answered. A request that names\
            \ the index answers from the generation that is live when it arrives,\
            \ so add `@` and this name to the index name to send a later request to\
            \ the same data. See [Names and generations](https://exofind.dev/reference/admin-api/#names-and-generations)."
          examples:
          - "2"
        freshness:
          type: string
          description: "A freshness token for the state the answer came from: the\
            \ generation, its commit, and the version of the search settings. Pass\
            \ it as `freshness.atLeast` on a later request, and that request is answered\
            \ from this state or a later one whichever node it lands on. Opaque; pass\
            \ it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
        tookMs:
          type: number
          format: double
          description: "Execution time for the search request in milliseconds, including\
            \ fractions of one."
          examples:
          - 7.412
    SearchSettingsDefinition:
      description: "Per-index settings that affect how searches are answered, sent\
        \ in full and replacing what was stored. Search settings belong to the index\
        \ name rather than to a generation, so promoting a generation preserves existing\
        \ search settings."
      examples:
      - ranking:
          signals:
          - field: purchases
            saturation:
              pivot: 50
            weight: 0.5
          tieBreakers:
          - field: sales
            direction: descending
        synonyms:
          products:
            rules:
            - equivalent:
              - laptop
              - notebook
            fields:
            - name
        fields:
          brand:
            interpret: {}
          size:
            values:
            - value: S
              order: 1
              labels:
                en: Small
                sv: Liten
            - value: M
              order: 2
              labels:
                en: Medium
                sv: Mellan
            - value: L
              order: 3
              labels:
                en: Large
                sv: Stor
      type: object
      properties:
        ranking:
          description: "The ranking searches run with instead of the definition's\
            \ ranking, in the same shape as the definition's `ranking`. While present,\
            \ it replaces the definition's ranking completely; an empty object turns\
            \ ranking off. A search request adds its own `signals` to whichever ranking\
            \ is in force, or replaces them with `signalsMode`. Validated against\
            \ the generation the index name answers from, using the same `index:ranking:*`\
            \ error codes that validate a definition's ranking."
          $ref: "#/components/schemas/Ranking"
        synonyms:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/QuerySynonyms"
          description: "Synonym sets applied to the text of a search, keyed by set\
            \ name. Unlike index-time synonym sets defined in an index definition,\
            \ which widen document values during indexing, query-time synonym sets\
            \ widen the search query and apply to every document already in the index."
        typoExclusions:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/TypoExclusions"
          description: "Words matched as they are spelled, keyed by list name. A word\
            \ on a list is looked up as it was typed, however much typo tolerance\
            \ the field it is searched in declares. Use a list for brand names and\
            \ model codes that sit inside text you want typo tolerant otherwise."
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/FieldSettings"
          description: "Settings that apply to one field, keyed by field name. A field\
            \ inside an object is keyed by its dotted path. Field names are validated\
            \ against the generation the index answers from at write time. See [Field\
            \ settings](https://exofind.dev/reference/admin-api/#field-settings)."
    SearchSettingsInfo:
      description: "The search settings of an index as stored, together with the observed\
        \ status reported by the answering node. See [Search settings](https://exofind.dev/reference/admin-api/#search-settings)."
      examples:
      - ranking:
          signals:
          - field: purchases
            saturation:
              pivot: 50
            weight: 0.5
          tieBreakers:
          - field: sales
            direction: descending
        version: 9f2c1a0b3d4e5f60
      type: object
      properties:
        ranking:
          description: The ranking searches run with instead of the definition's ranking.
            Omitted when the settings configure no ranking.
          $ref: "#/components/schemas/Ranking"
        synonyms:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/QuerySynonyms"
          description: "Synonym sets applied to the text of a search, keyed by set\
            \ name. Omitted when the settings configure no synonyms."
        typoExclusions:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/TypoExclusions"
          description: "Words matched as they are spelled, keyed by list name. Omitted\
            \ when the settings configure no typo exclusions."
        fields:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/FieldSettings"
          description: "Settings that apply to one field, keyed by field name. Omitted\
            \ when the settings configure no field."
        version:
          type: string
          description: "An identifier for the stored settings version, also returned\
            \ in the `ETag` header. Pass this value in the `If-Match` header on `PUT`\
            \ and `PATCH` requests to prevent overwriting concurrent updates; a mismatch\
            \ returns `412`."
          examples:
          - 9f2c1a0b3d4e5f60
        unsupportedFeatures:
          type: array
          items:
            type: string
          description: Present only when the answering node sets the settings aside
            because they use capabilities its version does not have. The node searches
            with the definition alone. Upgrade the node to put the settings in force.
        freshness:
          type: string
          description: "A freshness token for the state the change landed in. Present\
            \ on the answer to a `PUT` and a `PATCH`, and omitted on a `GET`. Pass\
            \ it as `freshness.atLeast` on a search, and the search is answered with\
            \ these settings in force whichever node it lands on. Opaque; pass it\
            \ back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMiIiJhYjEyY2QzNCI
    Signal:
      type: object
      description: "Document ranking signal used to adjust relevance scoring. Signals\
        \ apply only when results are ordered by relevance, so an explicit `sort`\
        \ overrides them. Targeting an unknown field returns `search:field_unknown`;\
        \ targeting a field without sorting enabled returns `search:usage_unsupported`;\
        \ a signal function unsupported by the field type returns `search:matcher:type_unsupported`.\
        \ See [Signals](https://exofind.dev/reference/search-api/#signals)."
      examples:
      - field: purchases
        saturation:
          pivot: 50
        weight: 0.5
      required:
      - field
      properties:
        field:
          type: string
          description: "Field to read the value from, as named in the index definition.\
            \ Must be a numeric or timestamp field with sorting enabled."
          examples:
          - purchases
        saturation:
          description: Ranks by how far the value rises above a pivot. For number
            fields.
          $ref: "#/components/schemas/Saturation"
        decay:
          description: Ranks by how long ago the value was. For timestamp fields.
          $ref: "#/components/schemas/Decay"
        linear:
          description: "Ranks by how far the value is toward a ceiling, as `value\
            \ / ceiling` held between `0` and `1`. For number fields holding a score\
            \ computed elsewhere."
          $ref: "#/components/schemas/Linear"
        weight:
          type: number
          format: float
          description: "How much the signal can lift a document at most, as a share\
            \ of its score."
          default: 1
    SignalDefinition:
      type: object
      description: "A single document attribute value multiplied into relevance. The\
        \ value is read from a sortable field, normalized to a value between `0` and\
        \ `1`, and applied to the score as `1 + weight * shape`. A document with no\
        \ value contributes `0`, ensuring a signal boosts a score by at most its configured\
        \ weight. Each signal must specify exactly one shape matching the field type."
      examples:
      - field: purchases
        saturation:
          pivot: 50
        weight: 0.5
      required:
      - field
      properties:
        field:
          type: string
          description: The field to read the value from. Must be a number or timestamp
            field with sorting enabled.
          examples:
          - purchases
        saturation:
          description: "Ranks by how far the value rises above a pivot. For `int32`,\
            \ `int64`, `float` and `double` fields."
          $ref: "#/components/schemas/RankingSaturation"
        decay:
          description: Ranks by how long ago the value was. For `timestamp` fields.
          $ref: "#/components/schemas/RankingDecay"
        linear:
          description: "Ranks by how far the value is toward a ceiling, as `value\
            \ / ceiling` held between `0` and `1`. For `int32`, `int64`, `float` and\
            \ `double` fields holding a score computed elsewhere, such as an engagement\
            \ score between `0` and `1`."
          $ref: "#/components/schemas/RankingLinear"
        weight:
          type: number
          format: float
          description: "How much the signal can lift a document at most, as a share\
            \ of its score. At `1`, a document at the top of the signal reaches twice\
            \ the score of one holding no value at all."
          default: 1
    SignalUsage:
      type: object
      description: Makes a number field a ranking signal that is refreshed in place
        through the document update action. Carries no configuration options.
      examples:
      - {}
    SignalsMode:
      description: "How search request signals meet the ranking configured on the\
        \ index: `\"add\"` ranks by both; `\"replace\"` ranks by the request's signals\
        \ alone."
      type: string
      enum:
      - add
      - replace
    Similarity:
      description: "Vector distance metric: `cosine`, `dot_product`, or `euclidean`.\
        \ `dot_product` requires unit-length normalized vectors."
      type: string
      enum:
      - cosine
      - dot_product
      - euclidean
    Sort:
      oneOf:
      - $ref: "#/components/schemas/FieldSort"
      - $ref: "#/components/schemas/ScoreSort"
      - $ref: "#/components/schemas/DistanceSort"
      description: "One step of the ordering of returned hits, structured as a tagged\
        \ union where `type` selects the sort type. The engine also accepts a sort\
        \ that omits `type`, which it reads as a `field` sort. If `order` is omitted,\
        \ score sorts default to descending and field sorts to ascending. Configured\
        \ index tie-breaker sorts are appended after the requested sorts. See [Sorts](https://exofind.dev/reference/search-api/#sorts)."
      examples:
      - field: name
        order: asc
      discriminator:
        propertyName: type
        mapping:
          field: "#/components/schemas/FieldSort"
          score: "#/components/schemas/ScoreSort"
          distance: "#/components/schemas/DistanceSort"
    SortOrder:
      description: "Direction values are ordered in: `asc` or `desc`."
      type: string
      enum:
      - asc
      - desc
    SortUsage:
      type: object
      description: Enables sorting search results by field value and configures value
        comparison.
      examples:
      - collation: locale
        missing: last
      properties:
        collation:
          description: Collation order used when comparing values. Applies only to
            string fields.
          default: locale
          $ref: "#/components/schemas/Collation"
        missing:
          description: Places documents without values first or last when sorting
            in ascending order.
          default: last
          $ref: "#/components/schemas/Missing"
    Source:
      description: "How much of a document an index retains: `full` retains the document\
        \ in full, and `none` retains only the fields marked `stored`."
      type: string
      enum:
      - full
      - none
    Stemming:
      type: object
      description: Reduces words to a shared root.
      examples:
      - locale: sv
      properties:
        locale:
          type: string
          description: "BCP-47 locale whose rules to stem by. If omitted, uses the\
            \ stemmer for the locale of the value being analyzed."
          examples:
          - sv
    StopwordsFilter:
      type: object
      description: "Removes frequent words. At most one of `locale`, `words` and `named`\
        \ is given; an empty object uses the stopwords of the locale of the value\
        \ being analyzed."
      examples:
      - locale: sv
      properties:
        locale:
          type: string
          description: BCP-47 locale whose stopwords to remove.
          examples:
          - sv
        words:
          type: array
          items:
            type: string
          description: A list of words to remove.
        named:
          type: string
          description: Name of a stopword list defined under the index's `resources`.
          examples:
          - brands
    Stored:
      type: string
      enum:
      - synced
      - incomplete
      - missing
    StringFieldDefinition:
      required:
      - type
      description: "Represents text data. Field usages are opt-in, each enabled by\
        \ including its configuration object. An empty object enables a usage with\
        \ engine defaults. See [`string`](https://exofind.dev/reference/field-types/#string)."
      examples:
      - type: string
        stored: true
        filter: {}
        matching:
          highlight: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - string
        role:
          description: "Specifies a field role that applies a preset combination of\
            \ usages. The role expands into explicit field properties before the definition\
            \ is stored, and any property set alongside the role is preserved as given.\
            \ Supported roles per type are listed under [Field roles](https://exofind.dev/reference/field-types/#field-roles)."
          $ref: "#/components/schemas/Role"
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
        keyword:
          description: Configures exact-match normalization for filtering.
          $ref: "#/components/schemas/Keyword"
        matching:
          description: Enables full-text search with analyzed terms.
          $ref: "#/components/schemas/TextUsage"
        autocomplete:
          description: Enables prefix matching for as-you-type search queries. A field
            defined only for `autocomplete` does not support phrase matching.
          $ref: "#/components/schemas/TextUsage"
        hierarchy:
          description: "Enables path hierarchy matching (for example, `Men/Shoes/Running`).\
            \ Facets on hierarchy fields return nested counts per level, and the `under`\
            \ matcher filters to a level and all sub-levels."
          $ref: "#/components/schemas/Hierarchy"
      type: object
    Suggest:
      type: object
      description: Suggests the values of the field while a search is typed. Carries
        no configuration options.
      examples:
      - {}
    SuggestRequest:
      description: "Asks what to search for, from the text typed so far. The suggestions\
        \ are the values of the fields the search settings of the index opt in with\
        \ `suggest`, that start with the text, the most common first. All properties\
        \ are optional; an empty request answers the most common values."
      examples:
      - text: adi
        filters:
        - field: category
          match:
            value: Shoes
        limit: 5
      type: object
      properties:
        text:
          type: string
          description: "What has been typed so far. Compared with the start of each\
            \ whole value, folded in case and Unicode form, so `rö` finds `Röd` and\
            \ `air` does not find `Nike Air Max`, and with the labels the search settings\
            \ declare for the values in the locale of the request. If omitted or blank,\
            \ the most common values are answered with `typed` at `0`."
          examples:
          - adi
        locale:
          type: string
          description: "BCP-47 locale tag used to read locale-specific fields and\
            \ to pick the labels of declared values, as for a search. If omitted,\
            \ uses each field's default locale."
          examples:
          - sv
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Clause"
          description: "Refinement clauses, in the same shape as the `filters` of\
            \ a search, such as the category a search box is scoped to. The counts\
            \ are the ones a facet of a search under the same filters answers. A filter\
            \ on a suggested field is left out of that field's own counts, so a brand\
            \ already ticked keeps the other brands suggestable (see [Facets](https://exofind.dev/reference/search-api/#facets)).\
            \ The clauses count against `EXOFIND_SEARCH_MAX_CLAUSES` and `EXOFIND_SEARCH_MAX_CLAUSE_DEPTH`,\
            \ as for a search."
        limit:
          type: integer
          format: int32
          minimum: 1
          description: "Maximum number of suggestions to return, at most `EXOFIND_SUGGEST_MAX_LIMIT`."
          default: 5
        typos:
          description: "Whether a value one mistake away from the text may be suggested\
            \ when fewer values than `limit` start with it. `auto` suggests them after\
            \ the values the text starts, once the text is at least five characters\
            \ long; a mistake is a character inserted, dropped, replaced, or two adjacent\
            \ ones swapped, never in the first character. Such a suggestion carries\
            \ `corrected: true` and `typed: 0`. `off` never suggests them."
          default: auto
          $ref: "#/components/schemas/SuggestTypos"
        freshness:
          description: "What the request demands of the state it is answered from.\
            \ Omit it to be answered from what the node holds. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          $ref: "#/components/schemas/Freshness"
    SuggestResponse:
      description: "The suggestions for the typed text, the most common first, with\
        \ every suggestion the text starts before the ones found a mistake away."
      examples:
      - suggestions:
        - text: adidas
          typed: 3
          field: brand
          value: adidas
          count: 87
        - text: Adidas Originals
          typed: 3
          field: brand
          value: Adidas Originals
          count: 12
        generation: "2"
        tookMs: 0.412
      type: object
      properties:
        suggestions:
          type: array
          items:
            $ref: "#/components/schemas/Suggestion"
          description: "The suggestions, the most common first and limited to the\
            \ requested maximum."
        generation:
          type: string
          description: "Name of the generation that answered. A request that names\
            \ the index answers from the generation that is live when it arrives,\
            \ so add `@` and this name to the index name to send a later request to\
            \ the same data. See [Names and generations](https://exofind.dev/reference/admin-api/#names-and-generations)."
          examples:
          - "2"
        freshness:
          type: string
          description: "A freshness token for the state the answer came from: the\
            \ generation, its commit, and the version of the search settings. Pass\
            \ it as `freshness.atLeast` on a later request, and that request is answered\
            \ from this state or a later one whichever node it lands on. Opaque; pass\
            \ it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
        tookMs:
          type: number
          format: double
          description: "Execution time for the request in milliseconds, including\
            \ fractions of one."
          examples:
          - 0.412
    SuggestTypos:
      description: "Typo tolerance: `auto` suggests values one mistake away from a\
        \ text of at least five characters when fewer values than the limit start\
        \ with it, `off` never does."
      type: string
      enum:
      - auto
      - "off"
    Suggestion:
      type: object
      description: "One thing to search for: a value of a suggested field, shown by\
        \ its label where the search settings declare one, with how much of it was\
        \ typed and how many documents hold it."
      examples:
      - text: adidas
        typed: 3
        field: brand
        value: adidas
        label: Adidas
        count: 87
      properties:
        text:
          type: string
          description: "What to show and to search for: the label of the value in\
            \ the locale of the request where the search settings declare one, or\
            \ the value itself. Where the typed text starts the value but not its\
            \ label, the value is shown, so that `typed` says something true. Sending\
            \ it as the `text` of a search finds the value, and a `corrected` suggestion\
            \ finds it exactly."
          examples:
          - adidas
        typed:
          type: integer
          format: int32
          description: "How many characters at the start of `text` the typed text\
            \ covers, so the part typed can be marked apart from the part that completes\
            \ it. Counted on `text` as answered, not on what was typed, so `RÖ` typed\
            \ against `Röd` covers 2. `0` when the suggestion was found a mistake\
            \ away, and for a blank text."
          examples:
          - 3
        corrected:
          type: boolean
          description: '`true` when the suggestion was found one mistake away from
            the text instead of starting with it. Omitted otherwise.'
        field:
          type: string
          description: "The field the value is held by, as declared in the index definition,\
            \ which a `field` clause filtering on the value names."
          examples:
          - brand
        value:
          description: "The value as the field stores it, which a filter on the field\
            \ matches."
          examples:
          - adidas
        label:
          type: string
          description: "The label the search settings of the index declare for the\
            \ value, in the locale of the request, falling back to the field's default\
            \ locale. Omitted when the settings declare no label for the value."
          examples:
          - Adidas
        count:
          type: integer
          format: int64
          description: Number of documents holding the value under the filters of
            the request.
          examples:
          - 87
    Synonyms:
      type: object
      description: Synonym rules applied during indexing. Modifying a synonym set
        applies only to documents indexed after the change.
      examples:
      - rules:
        - equivalent:
          - car
          - automobile
        - mapping:
            from:
            - ny
            to:
            - new york
      required:
      - rules
      properties:
        rules:
          type: array
          items:
            $ref: "#/components/schemas/Rule"
          description: The list of rules for the synonym set.
    SynonymsFilter:
      type: object
      description: "Expands tokens with synonyms from a synonym set defined in `resources`.\
        \ Applied when a value is indexed, not when the text of a search is analyzed.\
        \ See [Applying a synonym set to a field](https://exofind.dev/reference/analysis/#applying-a-synonym-set-to-a-field)."
      examples:
      - named: cars
      required:
      - named
      properties:
        named:
          type: string
          description: Name of a synonym set defined under the index's `resources`.
          examples:
          - cars
    TextClause:
      required:
      - type
      - text
      description: "Matches query text across one or more fields. Phrase queries operate\
        \ within a single field and match terms exactly as typed, regardless of field\
        \ `typoTolerance`. Fields defined only for `autocomplete` do not support phrase\
        \ matching. See [`text`](https://exofind.dev/reference/search-api/#text)."
      examples:
      - type: text
        text: silent spr
        fields:
          name: 3
          description: null
      properties:
        type:
          description: Selects the clause type.
          type: string
          enum:
          - text
        text:
          type: string
          description: The query text to match.
          examples:
          - silent spr
        fields:
          type: object
          additionalProperties:
            type: number
            format: float
          description: "Object mapping field names to score weights. A field mapped\
            \ to `null` uses the weight from its field definition. If omitted, searches\
            \ all searchable fields, skipping autocomplete-only fields."
        match:
          description: Term matching mode. `phrase` requires terms to appear in exact
            order and adjacent; `user` parses search syntax such as quotes and negation.
          default: all
          $ref: "#/components/schemas/Match"
        join:
          description: "How the parts of `user` text combine: `all` requires every\
            \ word and every quoted phrase, `any` accepts a document that holds one\
            \ of them. Excluded terms (`-word`) always apply, and a filter read out\
            \ of the text is one of the parts. Setting `join` with any other `match`\
            \ returns `search:clause:join_unsupported`. See [Reading what was typed](https://exofind.dev/reference/search-api/#reading-what-was-typed)."
          default: all
          $ref: "#/components/schemas/Join"
        prefix:
          description: Prefix matching behavior on the final query term. `last_token`
            matches the trailing word as a prefix; `off` requires an exact word match.
          default: last_token
          $ref: "#/components/schemas/Prefix"
        typos:
          description: Typo tolerance handling. `auto` follows each field's `typoTolerance`
            configuration; `off` disables typo tolerance for the clause.
          default: auto
          $ref: "#/components/schemas/TyposMode"
        slop:
          type: integer
          format: int32
          description: "Number of intervening words permitted between terms in a phrase,\
            \ without changing their relative order. Setting `slop` above `0` with\
            \ `\"match\": \"all\"` or `\"match\": \"any\"` returns `search:clause:slop_unsupported`."
          default: 0
        relax:
          description: "Query relaxation strategy applied only when the query returns\
            \ zero matches. See [Finding something rather than nothing](https://exofind.dev/reference/search-api/#finding-something-rather-than-nothing)."
          default: unmatched
          $ref: "#/components/schemas/Relax"
        combine:
          description: "Scope for multi-field term matching. `term` evaluates each\
            \ term across all targeted fields, so terms may appear in different fields;\
            \ `field` requires a single field to satisfy `match` on its own. Ignored\
            \ by phrase queries."
          default: term
          $ref: "#/components/schemas/Combine"
        interpret:
          description: "Whether parts of `user` text are read as filters on the fields\
            \ of the index, given as a mode or as the targets to read on. See [Reading\
            \ numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          default: auto
          $ref: "#/components/schemas/Interpret"
      type: object
    TextMatcher:
      required:
      - type
      - text
      description: Matches text within a single field using field-level analysis.
      examples:
      - type: text
        text: silent spring
        match: phrase
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - text
        text:
          type: string
          description: The query text to match.
        match:
          description: Term matching mode. `phrase` requires terms to appear in exact
            order and adjacent; `user` parses search syntax such as quotes and negation.
          default: all
          $ref: "#/components/schemas/Match"
        join:
          description: "How the parts of `user` text combine: `all` requires every\
            \ word and every quoted phrase, `any` accepts a document that holds one\
            \ of them. Excluded terms (`-word`) always apply. Setting `join` with\
            \ any other `match` returns `search:clause:join_unsupported`."
          default: all
          $ref: "#/components/schemas/Join"
        prefix:
          description: Prefix matching behavior on the final query term. `last_token`
            matches the trailing word as a prefix; `off` requires an exact word match.
          default: last_token
          $ref: "#/components/schemas/Prefix"
        typos:
          description: Typo tolerance handling. `auto` follows the field's `typoTolerance`
            configuration; `off` disables typo tolerance for the matcher.
          default: auto
          $ref: "#/components/schemas/TyposMode"
        slop:
          type: integer
          format: int32
          description: "Number of intervening words permitted between terms in a phrase,\
            \ without changing their relative order. Only applies to `phrase` queries\
            \ or quoted phrases in `user` mode."
          default: 0
        relax:
          description: "Query relaxation strategy applied only when the query returns\
            \ zero matches. See [Finding something rather than nothing](https://exofind.dev/reference/search-api/#finding-something-rather-than-nothing)."
          default: unmatched
          $ref: "#/components/schemas/Relax"
        interpret:
          description: "Whether parts of `user` text are read as filters on the fields\
            \ of the index: `auto` reads a number typed next to the unit of a number\
            \ field, or next to a comparative word such as `under`, as a filter on\
            \ that field; `off` takes every word as text. Whatever was read is reported\
            \ as `interpreted` beside the results. See [Reading numbers and units](https://exofind.dev/reference/search-api/#reading-numbers-and-units)."
          default: auto
          $ref: "#/components/schemas/InterpretMode"
      type: object
    TextUsage:
      type: object
      description: Configuration properties shared by the `matching` and `autocomplete`
        usages. The usage type determines the default analyzer configuration generated
        by the engine when no analyzer is explicitly provided.
      examples:
      - weight: 3
        highlight: {}
        typoTolerance: {}
        lengthNormalization: strong
      properties:
        analyzer:
          description: "Specifies how the text of this usage is analyzed. If omitted,\
            \ the engine generates an analyzer based on the field usage and locale.\
            \ See [Analysis](https://exofind.dev/reference/analysis/)."
          $ref: "#/components/schemas/AnalyzerDefinition"
        weight:
          type: number
          format: float
          description: Relative score weight of hits in this field when querying across
            multiple fields.
          default: 1
        highlight:
          description: Enables highlighted snippet extraction in search responses.
            Text is stored for highlighting regardless of the `stored` property. Highlighting
            targets `matching` when defined; `highlight` on `autocomplete` takes effect
            only when `matching` is omitted.
          $ref: "#/components/schemas/HighlightUsage"
        typoTolerance:
          description: "Enables typo tolerance for matching search terms, including\
            \ prefixes currently being typed."
          $ref: "#/components/schemas/TypoTolerance"
        decompound:
          description: "Controls compound word splitting in the engine-generated chain.\
            \ If omitted, splitting is determined by the locale of the value. Supported\
            \ only when using engine-generated analyzers; custom analyzers specified\
            \ via `analyzer` define their own decompounding behavior. See [Compound\
            \ words](https://exofind.dev/reference/analysis/#compound-words)."
          $ref: "#/components/schemas/Decompound"
        exact:
          description: Boosts documents where the query matches the full field value.
            Adjusts ranking only without modifying hit counts or facet distributions;
            analyzer normalization is applied before the comparison.
          $ref: "#/components/schemas/Exact"
        lengthNormalization:
          description: Controls the field length penalty in ranking. Changes take
            effect at search time without reindexing.
          default: moderate
          $ref: "#/components/schemas/LengthNormalization"
    TieBreaker:
      type: object
      description: A tie-breaking rule using a field defined for sorting.
      examples:
      - field: sales
        direction: descending
      required:
      - field
      properties:
        field:
          type: string
          description: The field to break ties by. Must have `sort` enabled.
          examples:
          - sales
        direction:
          description: The sort direction for breaking ties. Defaults to `descending`.
          default: descending
          $ref: "#/components/schemas/Direction"
    TimestampFieldDefinition:
      required:
      - type
      description: "Represents an instant in time formatted as an ISO 8601 date-time\
        \ string with a timezone offset (for example, `Z` or `+02:00`). Timestamps\
        \ are stored and compared at millisecond precision. Values representing the\
        \ same instant are identical for filtering and sorting; search results return\
        \ the original string format provided during ingestion. Documents containing\
        \ timestamps without timezone offsets are rejected."
      examples:
      - type: timestamp
        filter: {}
        sort: {}
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - timestamp
        role:
          description: "Specifies a field role that applies a preset combination of\
            \ usages. The role expands into explicit field properties before the definition\
            \ is stored, and any property set alongside the role is preserved as given.\
            \ Supported roles per type are listed under [Field roles](https://exofind.dev/reference/field-types/#field-roles)."
          $ref: "#/components/schemas/Role"
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: "Configures locale-specific field values so that analysis and\
            \ collation follow the locale of each value. On an index that declares\
            \ `locales`, configuring `{}` gives the field every declared locale, and\
            \ `only` narrows the field to a subset of those locales."
          $ref: "#/components/schemas/Locales"
        filter:
          description: "Enables filtering search results by exact field value. On\
            \ numeric and timestamp fields, filtering also enables range queries."
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Enables sorting search results by field value.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: "Enables value count aggregations. On numeric and timestamp\
            \ fields, it also enables range buckets."
          $ref: "#/components/schemas/FacetUsage"
      type: object
    TokenFilter:
      type: object
      description: "A transformation of the token stream. Exactly one kind is given,\
        \ selected by including its configuration."
      examples:
      - normalize: {}
      properties:
        normalize:
          description: Applies Unicode normalization and case folding to make analysis
            case-insensitive.
          $ref: "#/components/schemas/Normalize"
        stopwords:
          description: Removes frequent words.
          $ref: "#/components/schemas/StopwordsFilter"
        stemming:
          description: Reduces words to a shared root.
          $ref: "#/components/schemas/Stemming"
        asciiFolding:
          description: Converts non-ASCII characters to ASCII equivalents.
          $ref: "#/components/schemas/AsciiFolding"
        edgeNgram:
          description: Generates prefix n-grams for tokens within the specified character
            lengths.
          $ref: "#/components/schemas/EdgeNgram"
        ngram:
          description: Generates substring n-grams for tokens within the specified
            character lengths.
          $ref: "#/components/schemas/Ngram"
        synonyms:
          description: "Expands tokens with synonyms from a synonym set defined in\
            \ `resources`. Applied when a value is indexed, not when the text of a\
            \ search is analyzed."
          $ref: "#/components/schemas/SynonymsFilter"
        decompound:
          description: "Splits compound words into parts and retains the original\
            \ compound word. See [Compound words](https://exofind.dev/reference/analysis/#compound-words).\
            \ Applied at index time."
          $ref: "#/components/schemas/DecompoundFilter"
    Tokenizer:
      type: object
      description: "Specifies how text is split into tokens. Specify exactly one tokenizer\
        \ by including its configuration, for example `{ \"whitespace\": {} }`."
      examples:
      - icu: {}
      properties:
        icu:
          description: Segments text based on Unicode rules. This is the default tokenizer.
          $ref: "#/components/schemas/IcuTokenizer"
        whitespace:
          description: Splits text on whitespace characters.
          $ref: "#/components/schemas/WhitespaceTokenizer"
        keyword:
          description: Retains the entire input value as a single token.
          $ref: "#/components/schemas/KeywordTokenizer"
        letter:
          description: Splits text on non-letter characters.
          $ref: "#/components/schemas/LetterTokenizer"
    Total:
      type: object
      description: "Total match count, measured in whatever unit the search returns."
      examples:
      - count: 128
        exact: true
      properties:
        count:
          type: integer
          format: int64
          description: Total number of matching results.
          examples:
          - 128
        exact:
          type: boolean
          description: "Whether `count` is exact or a lower bound. Always true when\
            \ `\"total\": \"exact\"` is requested or when calculating facets."
    TotalMode:
      description: "Counting mode for the total matching document count: `\"estimate\"\
        ` counts until exceeding the returned window; `\"exact\"` counts every matching\
        \ document."
      type: string
      enum:
      - estimate
      - exact
    TypoExclusions:
      type: object
      description: "Words matched as they are spelled, regardless of the typo tolerance\
        \ configured on the searched fields."
      examples:
      - words:
        - adidas
        - X-15
        fields:
        - name
      properties:
        words:
          type: array
          items:
            type: string
          description: "The words, as typed. Words are read through the analysis chain\
            \ of each field they are excluded in. A word the chain leaves nothing\
            \ of (such as a stopword) excludes nothing, and a word the chain produces\
            \ several terms from excludes each of them."
        fields:
          type: array
          items:
            type: string
          description: "An optional list of field names the words are excluded in,\
            \ named as a search names them. If omitted, the list covers every field\
            \ searched as text. Field names are validated against the active generation\
            \ at write time; a generation promoted later that lacks a named field\
            \ applies typo tolerance as configured in the index definition."
    TypoTolerance:
      type: object
      description: Enables typo tolerance. Mixed alphanumeric words follow standard
        length thresholds.
      examples:
      - minLengthOneTypo: 5
        minLengthTwoTypos: 9
        prefixLength: 1
      properties:
        minLengthOneTypo:
          type: integer
          format: int32
          description: Minimum word length required to allow one typo.
          default: 5
        minLengthTwoTypos:
          type: integer
          format: int32
          description: "Minimum word length required to allow two typos. In `autocomplete`,\
            \ two typos are permitted only when explicitly configured."
          default: 9
        prefixLength:
          type: integer
          format: int32
          description: Number of leading characters that must match exactly.
          default: 1
        numbers:
          description: "Enables typo tolerance for digit-only words. If omitted, digit-only\
            \ words require exact matches regardless of length."
          $ref: "#/components/schemas/Numbers"
    TyposMode:
      description: "Typo tolerance handling: `auto` follows each field's `typoTolerance`\
        \ configuration, `off` disables it."
      type: string
      enum:
      - auto
      - "off"
    UnderMatcher:
      required:
      - type
      - path
      description: "Matches values at or below the specified path in a hierarchical\
        \ tree. Requires a field configured with [`hierarchy`](https://exofind.dev/reference/field-types/#string).\
        \ Path segments must match complete levels, so `Men/Sho` matches nothing where\
        \ a `prefix` matcher matches."
      examples:
      - type: under
        path: Men/Shoes
      properties:
        type:
          description: Selects the matcher type.
          type: string
          enum:
          - under
        path:
          type: string
          description: Path in the hierarchical tree to match at or below.
          examples:
          - Men/Shoes
      type: object
    UpdateRequest:
      description: "Field-level changes to documents already in an index. Every change\
        \ to one document is applied and validated as a whole. If validation fails,\
        \ the request is rejected and the document remains unchanged. For more information,\
        \ see [Update behavior](https://exofind.dev/reference/documents-api/#update-behavior)."
      examples:
      - documents:
        - id: "1"
          price: 34.5
          inStock: true
        - id: "2"
          price: 12.0
          variants[sku=V-2].price: 29.0
      type: object
      required:
      - documents
      properties:
        documents:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: "The changes, each carrying the primary key and the locations\
            \ to change, applied in the order provided. Every other key is a path:\
            \ a path with a value replaces what it names, a path set to `null` empties\
            \ what it names, and an omitted path leaves the existing value unchanged.\
            \ The path replaces exactly what it names: `variants` replaces every value\
            \ of the field, while `variants[sku=V-2].price` replaces one field inside\
            \ those values. For the whole syntax, see [Change paths](https://exofind.dev/reference/patch-paths/)."
    UpdateResponse:
      description: "The count of updated documents, and any keys or changes that were\
        \ skipped."
      examples:
      - updated: 1998
        missing: []
        failed: []
        freshness: AQoIcHJvZHVjdHMSATIYBw
      type: object
      properties:
        updated:
          type: integer
          format: int32
          description: The number of documents updated.
          examples:
          - 1998
        missing:
          type: array
          items:
            type: string
          description: "List of primary keys that were not found, in the order provided.\
            \ Each key is text, whatever type the key field declares: a whole-number\
            \ key `42` is returned as `\"42\"`. Send a key back in the `{key}` path\
            \ parameter or the `after` parameter as it came. For more information,\
            \ see [Primary keys on the wire](https://exofind.dev/reference/api-conventions/#primary-keys-on-the-wire).\
            \ Holds keys only when the request is sent with `?missing=skip`; a request\
            \ sent without it fails on the first missing key. Always present, and\
            \ empty when nothing was skipped."
        failed:
          type: array
          items:
            $ref: "#/components/schemas/DocumentFailure"
          description: "The changes the index refused, in the order sent. Holds entries\
            \ only when the request is sent with `?onError=skip`; a request sent without\
            \ it fails on the first refused change. A key nothing is indexed under\
            \ is reported under `missing` instead when the request is sent with `?missing=skip`.\
            \ Always present, and empty when nothing was skipped."
        freshness:
          type: string
          description: "A freshness token for the state the change lands in. Pass\
            \ it as `freshness.atLeast` on a search, or in the `X-Exofind-Freshness`\
            \ header of a read, and that request is answered only once the node holds\
            \ the change. Opaque; pass it back unchanged. See [Freshness](https://exofind.dev/reference/search-api/#freshness)."
          examples:
          - AQoIcHJvZHVjdHMSATIYBw
    VectorFieldDefinition:
      required:
      - type
      - dimensions
      description: "Represents an array of floating-point numbers searched by similarity\
        \ using the `knn` search clause. Vector fields do not support `filter`, `sort`,\
        \ `facet`, or `locales`. Vectors must be supplied in document payloads. See\
        \ [Search by vector](https://exofind.dev/how-to/search-by-vector/)."
      examples:
      - type: vector
        dimensions: 1536
        similarity: cosine
      properties:
        type:
          description: Selects the field type.
          type: string
          enum:
          - vector
        primaryKey:
          type: boolean
          description: "Marks the field as the unique document identifier. Documents\
            \ with matching primary keys overwrite existing documents. An index can\
            \ have at most one primary key. Primary key fields must be `required`\
            \ and cannot be `multiple`, locale-specific, or wildcard fields."
          default: false
        required:
          type: boolean
          description: "When `true`, the engine rejects documents that lack a value\
            \ for this field."
          default: false
        multiple:
          type: boolean
          description: "When `true`, the field accepts multiple values in a single\
            \ document. If `false`, the engine rejects documents containing multiple\
            \ values for the field."
          default: false
        stored:
          type: boolean
          description: "When `true`, the engine stores field values to return in search\
            \ results. This setting applies only when `source` is set to `none`, as\
            \ documents are otherwise preserved in full."
          default: false
        locales:
          description: Not supported on a vector field; setting it is rejected.
          $ref: "#/components/schemas/Locales"
        filter:
          description: Not supported on a vector field. Vector fields are searched
            by similarity using the `knn` search clause; setting it is rejected.
          $ref: "#/components/schemas/FilterUsage"
        sort:
          description: Not supported on a vector field; setting it is rejected.
          $ref: "#/components/schemas/SortUsage"
        facet:
          description: Not supported on a vector field; setting it is rejected.
          $ref: "#/components/schemas/FacetUsage"
        dimensions:
          type: integer
          format: int32
          description: Number of vector dimensions. Required. Cannot be modified after
            indexing documents.
          examples:
          - 1536
        similarity:
          description: Vector distance metric. `dot_product` requires unit-length
            normalized vectors.
          default: cosine
          $ref: "#/components/schemas/Similarity"
        hnsw:
          description: Hierarchical Navigable Small World index configuration.
          $ref: "#/components/schemas/Hnsw"
        quantization:
          description: Vector compression method.
          default: none
          $ref: "#/components/schemas/Quantization"
      type: object
    WhitespaceTokenizer:
      type: object
      description: Whitespace segmentation. Carries no options.
      examples:
      - {}
  securitySchemes:
    apiKey:
      type: http
      description: "An API key sent as a bearer token, such as `Authorization: Bearer\
        \ exok_4ff6b760264c1918_ePQcdT1O9HSATZoXfDbT8hhHGsP9VpZH`. A key carries grants\
        \ that pair permissions with index patterns; the permission each endpoint\
        \ needs is named beside it. Nodes running with `EXOFIND_AUTH_MODE=none` accept\
        \ requests without a credential, and a node with `EXOFIND_AUTH_ANONYMOUS_KEY`\
        \ set serves requests that carry none with the permissions of that key."
      scheme: bearer
tags:
- name: API keys
  description: "Creates, lists, and revokes the API keys of the deployment."
  externalDocs:
    description: Authentication reference
    url: https://exofind.dev/reference/auth/
- name: Documents
  description: "Reads, creates, updates, and deletes documents in an index."
  externalDocs:
    description: Documents API reference
    url: https://exofind.dev/reference/documents-api/
- name: Indexers
  description: Which node writes which index.
  externalDocs:
    description: Indexers reference
    url: https://exofind.dev/reference/admin-api/#indexers
- name: Indexes
  description: "Defines, reads, and deletes indexes and their generations."
  externalDocs:
    description: Admin API reference
    url: https://exofind.dev/reference/admin-api/
- name: Registry
  description: "Compares the index registry with what storage holds, and repairs it."
  externalDocs:
    description: Registry reference
    url: https://exofind.dev/reference/admin-api/#registry
- name: Reindexes
  description: Populates a new generation by copying documents from an existing generation
    inside the engine.
  externalDocs:
    description: Reindex reference
    url: https://exofind.dev/reference/admin-api/#reindex
- name: Search
  description: Finds documents in an index.
  externalDocs:
    description: Search API reference
    url: https://exofind.dev/reference/search-api/
- name: Search settings
  description: "Per-index search configuration, managed separately from index definitions."
  externalDocs:
    description: Search settings reference
    url: https://exofind.dev/reference/admin-api/#search-settings
paths:
  /v1alpha1/admin/indexers:
    get:
      summary: List indexer candidates and claims
      description: |-
        Lists candidate nodes competing to write indexes and the active writer claim for each index.

        Any node can serve this request from its local view of shared deployment state, including search-only nodes. The response can lag actual state by a few seconds. Indexes without an active claim are omitted until a write assigns a writer.

        If a credential lacks permissions for an index, that index is omitted from the claims list. On nodes using local storage, both lists are empty.

        Requires the `indexes.read` permission on at least one index. The `reader`, `writer` and `admin` roles include it.
      operationId: listIndexers
      tags:
      - Indexers
      responses:
        "200":
          description: The candidate nodes and the claims the key can see.
          content:
            application/json:
              examples:
                indexers:
                  value:
                    candidates:
                    - node: node-a-7f21
                      address: http://node-a:8080
                      expiresAt: 2026-08-21T10:15:30Z
                    claims:
                    - index: products
                      node: node-a-7f21
                      address: http://node-a:8080
                      expiresAt: 2026-08-21T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexerListResponse"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
        "503":
          description: |-
            Indexer leadership assignments could not be read from shared storage. Retrying the request is expected to work once storage responds.

            Error codes: `indexer:leadership_unreadable` - Leadership assignments could not be read from shared state storage. Send the request again once storage answers.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:leadership_unreadable
            when: Leadership assignments could not be read from shared state storage.
              Send the request again once storage answers.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: any-index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes:
    get:
      summary: List indexes
      description: |-
        Lists the indexes the deployment holds, with their generations and the live generation each answers for.

        Index listings omit indexes on which the key has no permissions rather than refusing the listing.

        `prefix` keeps the indexes whose name starts with it. `limit` caps the answer, and a listing cut short names the last index in `next`; pass it as `after` to read on. See [Listings](https://exofind.dev/reference/admin-api/#listings).

        Requires the `indexes.read` permission on at least one index. The `reader`, `writer` and `admin` roles include it.
      operationId: listIndexes
      tags:
      - Indexes
      parameters:
      - description: "The name to continue after, as the `next` field of the previous\
          \ response gave it. The named index is not included."
        example: products
        name: after
        in: query
        schema:
          type: string
      - description: "Most indexs to answer. Without it the whole listing is answered.\
          \ When more remain, the response carries the last name in `next`."
        schema:
          maximum: 1000
          minimum: 1
          type: integer
        name: limit
        in: query
      - description: Keeps only the indexs whose name starts with this text.
        name: prefix
        in: query
        schema:
          type: string
      responses:
        "200":
          description: "The indexes the key can see, ordered by name."
          content:
            application/json:
              examples:
                indexes:
                  value:
                    indexes:
                    - name: products
                      liveGeneration: "2"
                      generations:
                      - name: "1"
                        live: false
                        createdAt: 2026-08-16T11:02:07Z
                      - name: "2"
                        live: true
                        createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexListResponse"
        "400":
          description: |-
            The `limit` parameter is out of range.

            Error codes: `request:limit_out_of_range` - The `limit` parameter is not a whole number from 1 to 1000.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:limit_out_of_range
            when: The `limit` parameter is not a whole number from 1 to 1000.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: any-index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}:
    put:
      summary: Create or replace an index definition
      description: |-
        Sends a definition in full, replacing any previous definition. Any setting the body omits is removed. Repeating the request produces the same outcome.

        The target of the request depends on the name format. `books` creates the index with an initial generation named `1`, or updates the definition of the live generation; `books@2` creates that generation under an existing index, or updates its definition. A newly created generation contains no documents and is not live; the index continues serving from the previous live generation until `actions/promote` is called, and `PUT books@2` on an index that does not exist returns `404`.

        When the target generation already holds documents, a request is refused with `409` and `index:definition:incompatible` if the new definition changes how documents are indexed, such as enabling a usage on an existing field, changing an analyzer chain, editing a synonym set, or changing `type`, `primaryKey`, or `multiple`. The response includes one detail item per difference, each with the `path` of the field that caused it. Adding or removing a field, disabling a usage, and changing `stored`, `source`, `metadata`, `ranking`, or search-time settings are accepted.

        Requests run on the node that writes the index; a request received by another node is forwarded there.

        Requires the `indexes.write` permission on the index the path names. The `admin` role includes it.
      operationId: putIndex
      tags:
      - Indexes
      parameters:
      - description: "The index, which creates it or updates the live generation,\
          \ or one generation by name such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: Forces the update without reindexing existing documents. Existing
          documents continue to serve queries as indexed until they are reindexed.
          Has no effect on an empty generation.
        schema:
          type: boolean
          default: false
        name: allowStaleDocuments
        in: query
      - description: "Starts a reindex job filling the generation being created from\
          \ the live one, the way the reindex action would. One-shot: it is not stored\
          \ in the definition, and it is refused on a request that creates no generation.\
          \ `auto` also needs `indexes.promote`, because the job it starts promotes\
          \ the generation it filled; `manual` needs only `indexes.write`."
        schema:
          type: string
          enum:
          - auto
          - manual
        name: reindex
        in: query
      - description: "The expected definition version, as returned in a previous `ETag`\
          \ header. `*` asks only that the index exists. Several versions may be given,\
          \ separated by commas, and the header is satisfied while the stored version\
          \ is one of them; versions are compared exactly, so a weak tag (`W/\"...\"\
          `) matches none. An index that does not exist answers `404`, and a version\
          \ that no longer matches answers `412` instead of overwriting intermediate\
          \ changes."
        example: '"9f2c1a0b3d4e5f60"'
        name: If-Match
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            examples:
              definition:
                summary: A primary key and four searchable fields
                value:
                  fields:
                    id:
                      type: string
                      primaryKey: true
                      required: true
                    name:
                      type: string
                      matching: {}
                      sort: {}
                    category:
                      type: string
                      filter: {}
                      facet: {}
                    price:
                      type: float
                      filter: {}
                      sort: {}
                    published:
                      type: boolean
                      filter: {}
            schema:
              $ref: "#/components/schemas/IndexDefinition"
        required: true
      responses:
        "200":
          description: An existing definition was replaced. The new version is in
            the `ETag` header.
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "201":
          description: The index or generation was created. The version is in the
            `ETag` header and the location in `Location`.
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "400":
          description: |-
            The definition failed validation - the response details each problem - or the request asks for a reindex it cannot run.

            Error codes: `index:generation:reindex_without_creation` - `reindex` was given on a request that creates no generation. `request:body_required` - The request carries no definition. `request:value_required` - A property of the definition that needs a value is `null`. `index:field:analyzer:invalid` - The analyzer of a field is not exactly one of a preset, a custom chain and a named chain. `index:field:analyzer:component_invalid` - A component of a custom analysis chain is not exactly one kind. `index:field:analyzer:decompound_conflicting` - A field sets `decompound` beside a custom or named chain. A given chain says itself whether it splits, through a `decompound` component. `index:field:locales:locale_unknown` - A field names a locale the index does not declare in its `locales`. `index:field:locales:list_with_declaration` - A field lists its own `locales` while the index declares them. Narrow with `only` instead. `index:field:locales:only_without_declaration` - A field narrows with `only` while the index declares no `locales` to narrow. `index:field:locales:default_not_in_only` - A field narrows to locales that leave out the locale it defaults to. `index:locales:default_locale_required` - The `locales` of the index names no `defaultLocale`, which every field takes as its own. `index:field:role:type_unsupported` - A field has a role that no field of its type can answer for. `index:field:role:object_unsupported` - A field inside an object field has a role that cannot be used there. `index:ranking:signal:shape_invalid` - A ranking signal is not exactly one of `saturation`, `decay` and `linear`. `index:resources:analyzers:named` - An analysis chain in `resources` is itself `named`. The resources are where names are defined. `index:resources:synonyms:rule_invalid` - A rule of a synonym set in `resources` is not exactly one kind - equivalent words, or a one-way mapping. `index:resources:synonyms:one_sided` - A one-way synonym mapping carries no word on one of its sides. `index:resources:synonyms:words_too_few` - A rule of equivalent synonyms carries fewer than two words. `index:resources:synonyms:word_required` - A synonym is blank. `index:field:name_invalid` - A field name holds something other than letters, numbers, underscores and wildcards. To hold fields under a dotted path, declare an `object` field. `index:field:type_required` - A field declares no type. `index:field:type_unsupported` - A field has a type this version of the engine cannot index. `index:field:primary_key:wildcard_unsupported` - A field name with a wildcard is marked as the primary key. `index:field:primary_key:multiple_unsupported` - The primary key field is also `multiple`. `index:field:primary_key:type_unsupported` - The primary key field has a type that cannot be a primary key. `index:schema:primary_key_duplicate` - More than one field is marked as the primary key. `index:schema:primary_key_locales_unsupported` - The primary key field is locale specific. `index:schema:primary_key_not_required` - The primary key field is marked as not required. `index:schema:features_unsupported` - The definition needs engine features this version does not have. The `features` argument names them. `index:field:required:wildcard_unsupported` - A field name with a wildcard is marked as required. `index:field:sort:multiple_unsupported` - A field is both `sortable` and `multiple`. `index:field:sort:type_unsupported` - A field is `sortable` and its type cannot be sorted on. `index:field:facet:type_unsupported` - A field is faceted and its type cannot be counted per value. `index:field:signal:type_unsupported` - A field is a signal and its type cannot be refreshed in place. `index:field:signal:usage_conflicting` - A signal field is also declared for a usage that would go stale on every refresh. `index:field:signal:wildcard_unsupported` - A field name with a wildcard is a signal. A signal is refreshed by the name it was declared under. `index:field:locales:locale_unsupported` - A field names a locale this version of the engine does not support. `index:field:locales:fallback_without_index` - A field takes part in locale fallback and the index declares none. `index:locale_fallback:locale_fields_required` - The index falls back between locales and no field of it is locale specific. `index:locale_fallback:locale_duplicate` - A locale is fallen back to more than once. `index:locale_fallback:locale_not_held` - A locale is fallen back to that no field of the index holds values in. `index:locale_fallback:locale_unsupported` - A locale is fallen back to that this version of the engine does not support. `index:field:analyzer:conflicting` - A usage carries an analysis chain and also names one in `resources`. Give at most one of the two. `index:field:analyzer:chain_unknown` - A usage names an analysis chain that `resources` does not define. `index:field:analyzer:stopwords_unknown` - An analysis chain names a stopword list that `resources` does not define. `index:field:analyzer:synonyms_unknown` - An analysis chain names a synonym set that `resources` does not define. `index:field:analyzer:locale_unsupported` - An analysis chain names a locale this version of the engine does not support. `index:field:analyzer:decompound_locale_unsupported` - An analysis chain splits compounds by a locale this version of the engine has no decompounding data for. `index:field:analyzer:grams_invalid` - An n-gram component asks for sizes below one, or a shortest longer than its longest. `index:field:analyzer:pattern_invalid` - A pattern replacement component carries something that is not a valid regular expression. `index:field:matching:weight_out_of_range` - The weight of matching is not above zero. `index:field:matching:typo_min_length_out_of_range` - The shortest word that may hold a typo is below one character. `index:field:matching:typo_lengths_conflicting` - A word is long enough for two typos before it is long enough for one. `index:field:matching:typo_prefix_out_of_range` - The prefix matched exactly under typo tolerance is below zero. `index:field:autocomplete:weight_out_of_range` - The weight of autocomplete is not above zero. `index:field:exact:boost_out_of_range` - The boost of a whole-value match is not above zero. `index:field:hierarchy:separator_invalid` - The separator between the levels of a path is empty. Leave it out for `/`. `index:field:sort:collation_unsupported` - A timestamp field declares a collation, which means nothing when sorting one. `index:field:number:unit_invalid` - The `unit` of a number field is not text. `index:field:number:bound_invalid` - A validation bound of a number field is not a finite number. `index:field:number:bounds_conflicting` - The `min` of a number field is above its `max`. `index:field:vector:dimensions_required` - A vector field declares no dimensions. `index:field:vector:dimensions_out_of_range` - The dimensions of a vector field are outside 1 to the maximum the engine indexes. `index:field:vector:hnsw_m_out_of_range` - The HNSW neighbour count `m` is outside the range the engine builds. `index:field:vector:hnsw_ef_construction_out_of_range` - The HNSW `ef_construction` is outside the range the engine builds. `index:field:vector:multiple_unsupported` - A vector field is `multiple`. A vector field holds one vector per document. `index:field:vector:locales_unsupported` - A vector field is locale specific. `index:field:vector:filter_unsupported` - A vector field is declared for `filter`. Search a vector field with a `knn` clause. `index:field:object:fields_required` - An object field declares no fields. `index:field:object:usage_unsupported` - An object field is declared for a usage it holds no value of its own to answer. `index:field:object:inner_usage_unsupported` - A field inside an object is declared for a usage that is not supported there. `index:field:object:mode_required` - A list of objects declares no `mode`. Use `nested` when a search asks that conditions hold inside one value, `flattened` when the values are only structure. `index:field:object:mode_without_multiple` - A single object declares a `mode`, which applies only together with `multiple`. `index:field:object:nested_in_nested` - A nested list of objects sits below another nested list. Keep the inner list `flattened`, or lift it out. `index:field:object:flattened_sort_unsupported` - A field inside a flattened list of objects is declared for `sort`. `index:field:object:flattened_stored_unsupported` - A field inside a flattened list of objects is declared for `stored`. `index:field:object:key_without_multiple` - A single object declares a `key`, which applies only together with `multiple`. `index:field:object:key_unknown` - The `key` of an object field names a field the object does not hold. `index:field:object:key_invalid` - The `key` of an object field names a field that cannot say which value is which. `index:ranking:field_unknown` - A tie-breaker names a field the definition does not declare. `index:ranking:wildcard_unsupported` - A tie-breaker names fields with a wildcard. A tie-breaker orders by one field. `index:ranking:field_not_sortable` - A tie-breaker names a field that is not defined for sorting. `index:ranking:field_duplicate` - Two tie-breakers name the same field. `index:ranking:signal:field_unknown` - A ranking signal names a field the definition does not declare. `index:ranking:signal:wildcard_unsupported` - A ranking signal names fields with a wildcard. A signal reads one field. `index:ranking:signal:field_not_sortable` - A ranking signal names a field that is not defined for sorting, so it holds no value to read. `index:ranking:signal:shape_required` - A ranking signal does not say how the value it reads counts. `index:ranking:signal:shape_unsupported` - A ranking signal reads its field with a shape the type of the field holds nothing for. `index:ranking:signal:pivot_out_of_range` - The `pivot` of a saturation signal is not a number above zero. `index:ranking:signal:half_life_out_of_range` - The `halfLife` of a decay signal is not longer than nothing. `index:ranking:signal:ceiling_out_of_range` - The `ceiling` of a linear signal is not a number above zero. `index:ranking:signal:weight_out_of_range` - The `weight` of a ranking signal is below zero. `index:name_invalid` - The name in the path is not a valid index name. `index:generation:name_invalid` - The name in the path names a generation that is not a valid generation name. `index:generation:not_creatable` - The name in the path names a generation of an index that does not exist. Create the index first.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:generation:reindex_without_creation
            when: '`reindex` was given on a request that creates no generation.'
          - code: request:body_required
            when: The request carries no definition.
          - code: request:value_required
            when: A property of the definition that needs a value is `null`.
          - code: index:field:analyzer:invalid
            when: "The analyzer of a field is not exactly one of a preset, a custom\
              \ chain and a named chain."
          - code: index:field:analyzer:component_invalid
            when: A component of a custom analysis chain is not exactly one kind.
          - code: index:field:analyzer:decompound_conflicting
            when: "A field sets `decompound` beside a custom or named chain. A given\
              \ chain says itself whether it splits, through a `decompound` component."
          - code: index:field:locales:locale_unknown
            when: A field names a locale the index does not declare in its `locales`.
          - code: index:field:locales:list_with_declaration
            when: A field lists its own `locales` while the index declares them. Narrow
              with `only` instead.
          - code: index:field:locales:only_without_declaration
            when: A field narrows with `only` while the index declares no `locales`
              to narrow.
          - code: index:field:locales:default_not_in_only
            when: A field narrows to locales that leave out the locale it defaults
              to.
          - code: index:locales:default_locale_required
            when: "The `locales` of the index names no `defaultLocale`, which every\
              \ field takes as its own."
          - code: index:field:role:type_unsupported
            when: A field has a role that no field of its type can answer for.
          - code: index:field:role:object_unsupported
            when: A field inside an object field has a role that cannot be used there.
          - code: index:ranking:signal:shape_invalid
            when: "A ranking signal is not exactly one of `saturation`, `decay` and\
              \ `linear`."
          - code: index:resources:analyzers:named
            when: An analysis chain in `resources` is itself `named`. The resources
              are where names are defined.
          - code: index:resources:synonyms:rule_invalid
            when: "A rule of a synonym set in `resources` is not exactly one kind\
              \ - equivalent words, or a one-way mapping."
          - code: index:resources:synonyms:one_sided
            when: A one-way synonym mapping carries no word on one of its sides.
          - code: index:resources:synonyms:words_too_few
            when: A rule of equivalent synonyms carries fewer than two words.
          - code: index:resources:synonyms:word_required
            when: A synonym is blank.
          - code: index:field:name_invalid
            when: "A field name holds something other than letters, numbers, underscores\
              \ and wildcards. To hold fields under a dotted path, declare an `object`\
              \ field."
          - code: index:field:type_required
            when: A field declares no type.
          - code: index:field:type_unsupported
            when: A field has a type this version of the engine cannot index.
          - code: index:field:primary_key:wildcard_unsupported
            when: A field name with a wildcard is marked as the primary key.
          - code: index:field:primary_key:multiple_unsupported
            when: The primary key field is also `multiple`.
          - code: index:field:primary_key:type_unsupported
            when: The primary key field has a type that cannot be a primary key.
          - code: index:schema:primary_key_duplicate
            when: More than one field is marked as the primary key.
          - code: index:schema:primary_key_locales_unsupported
            when: The primary key field is locale specific.
          - code: index:schema:primary_key_not_required
            when: The primary key field is marked as not required.
          - code: index:schema:features_unsupported
            when: The definition needs engine features this version does not have.
              The `features` argument names them.
          - code: index:field:required:wildcard_unsupported
            when: A field name with a wildcard is marked as required.
          - code: index:field:sort:multiple_unsupported
            when: A field is both `sortable` and `multiple`.
          - code: index:field:sort:type_unsupported
            when: A field is `sortable` and its type cannot be sorted on.
          - code: index:field:facet:type_unsupported
            when: A field is faceted and its type cannot be counted per value.
          - code: index:field:signal:type_unsupported
            when: A field is a signal and its type cannot be refreshed in place.
          - code: index:field:signal:usage_conflicting
            when: A signal field is also declared for a usage that would go stale
              on every refresh.
          - code: index:field:signal:wildcard_unsupported
            when: A field name with a wildcard is a signal. A signal is refreshed
              by the name it was declared under.
          - code: index:field:locales:locale_unsupported
            when: A field names a locale this version of the engine does not support.
          - code: index:field:locales:fallback_without_index
            when: A field takes part in locale fallback and the index declares none.
          - code: index:locale_fallback:locale_fields_required
            when: The index falls back between locales and no field of it is locale
              specific.
          - code: index:locale_fallback:locale_duplicate
            when: A locale is fallen back to more than once.
          - code: index:locale_fallback:locale_not_held
            when: A locale is fallen back to that no field of the index holds values
              in.
          - code: index:locale_fallback:locale_unsupported
            when: A locale is fallen back to that this version of the engine does
              not support.
          - code: index:field:analyzer:conflicting
            when: A usage carries an analysis chain and also names one in `resources`.
              Give at most one of the two.
          - code: index:field:analyzer:chain_unknown
            when: A usage names an analysis chain that `resources` does not define.
          - code: index:field:analyzer:stopwords_unknown
            when: An analysis chain names a stopword list that `resources` does not
              define.
          - code: index:field:analyzer:synonyms_unknown
            when: An analysis chain names a synonym set that `resources` does not
              define.
          - code: index:field:analyzer:locale_unsupported
            when: An analysis chain names a locale this version of the engine does
              not support.
          - code: index:field:analyzer:decompound_locale_unsupported
            when: An analysis chain splits compounds by a locale this version of the
              engine has no decompounding data for.
          - code: index:field:analyzer:grams_invalid
            when: "An n-gram component asks for sizes below one, or a shortest longer\
              \ than its longest."
          - code: index:field:analyzer:pattern_invalid
            when: A pattern replacement component carries something that is not a
              valid regular expression.
          - code: index:field:matching:weight_out_of_range
            when: The weight of matching is not above zero.
          - code: index:field:matching:typo_min_length_out_of_range
            when: The shortest word that may hold a typo is below one character.
          - code: index:field:matching:typo_lengths_conflicting
            when: A word is long enough for two typos before it is long enough for
              one.
          - code: index:field:matching:typo_prefix_out_of_range
            when: The prefix matched exactly under typo tolerance is below zero.
          - code: index:field:autocomplete:weight_out_of_range
            when: The weight of autocomplete is not above zero.
          - code: index:field:exact:boost_out_of_range
            when: The boost of a whole-value match is not above zero.
          - code: index:field:hierarchy:separator_invalid
            when: The separator between the levels of a path is empty. Leave it out
              for `/`.
          - code: index:field:sort:collation_unsupported
            when: "A timestamp field declares a collation, which means nothing when\
              \ sorting one."
          - code: index:field:number:unit_invalid
            when: The `unit` of a number field is not text.
          - code: index:field:number:bound_invalid
            when: A validation bound of a number field is not a finite number.
          - code: index:field:number:bounds_conflicting
            when: The `min` of a number field is above its `max`.
          - code: index:field:vector:dimensions_required
            when: A vector field declares no dimensions.
          - code: index:field:vector:dimensions_out_of_range
            when: The dimensions of a vector field are outside 1 to the maximum the
              engine indexes.
          - code: index:field:vector:hnsw_m_out_of_range
            when: The HNSW neighbour count `m` is outside the range the engine builds.
          - code: index:field:vector:hnsw_ef_construction_out_of_range
            when: The HNSW `ef_construction` is outside the range the engine builds.
          - code: index:field:vector:multiple_unsupported
            when: A vector field is `multiple`. A vector field holds one vector per
              document.
          - code: index:field:vector:locales_unsupported
            when: A vector field is locale specific.
          - code: index:field:vector:filter_unsupported
            when: A vector field is declared for `filter`. Search a vector field with
              a `knn` clause.
          - code: index:field:object:fields_required
            when: An object field declares no fields.
          - code: index:field:object:usage_unsupported
            when: An object field is declared for a usage it holds no value of its
              own to answer.
          - code: index:field:object:inner_usage_unsupported
            when: A field inside an object is declared for a usage that is not supported
              there.
          - code: index:field:object:mode_required
            when: "A list of objects declares no `mode`. Use `nested` when a search\
              \ asks that conditions hold inside one value, `flattened` when the values\
              \ are only structure."
          - code: index:field:object:mode_without_multiple
            when: "A single object declares a `mode`, which applies only together\
              \ with `multiple`."
          - code: index:field:object:nested_in_nested
            when: "A nested list of objects sits below another nested list. Keep the\
              \ inner list `flattened`, or lift it out."
          - code: index:field:object:flattened_sort_unsupported
            when: A field inside a flattened list of objects is declared for `sort`.
          - code: index:field:object:flattened_stored_unsupported
            when: A field inside a flattened list of objects is declared for `stored`.
          - code: index:field:object:key_without_multiple
            when: "A single object declares a `key`, which applies only together with\
              \ `multiple`."
          - code: index:field:object:key_unknown
            when: The `key` of an object field names a field the object does not hold.
          - code: index:field:object:key_invalid
            when: The `key` of an object field names a field that cannot say which
              value is which.
          - code: index:ranking:field_unknown
            when: A tie-breaker names a field the definition does not declare.
          - code: index:ranking:wildcard_unsupported
            when: A tie-breaker names fields with a wildcard. A tie-breaker orders
              by one field.
          - code: index:ranking:field_not_sortable
            when: A tie-breaker names a field that is not defined for sorting.
          - code: index:ranking:field_duplicate
            when: Two tie-breakers name the same field.
          - code: index:ranking:signal:field_unknown
            when: A ranking signal names a field the definition does not declare.
          - code: index:ranking:signal:wildcard_unsupported
            when: A ranking signal names fields with a wildcard. A signal reads one
              field.
          - code: index:ranking:signal:field_not_sortable
            when: "A ranking signal names a field that is not defined for sorting,\
              \ so it holds no value to read."
          - code: index:ranking:signal:shape_required
            when: A ranking signal does not say how the value it reads counts.
          - code: index:ranking:signal:shape_unsupported
            when: A ranking signal reads its field with a shape the type of the field
              holds nothing for.
          - code: index:ranking:signal:pivot_out_of_range
            when: The `pivot` of a saturation signal is not a number above zero.
          - code: index:ranking:signal:half_life_out_of_range
            when: The `halfLife` of a decay signal is not longer than nothing.
          - code: index:ranking:signal:ceiling_out_of_range
            when: The `ceiling` of a linear signal is not a number above zero.
          - code: index:ranking:signal:weight_out_of_range
            when: The `weight` of a ranking signal is below zero.
          - code: index:name_invalid
            when: The name in the path is not a valid index name.
          - code: index:generation:name_invalid
            when: The name in the path names a generation that is not a valid generation
              name.
          - code: index:generation:not_creatable
            when: The name in the path names a generation of an index that does not
              exist. Create the index first.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.write` permission.
        "404":
          description: |-
            `If-Match` was sent for an index that does not exist, `books@2` named an index that does not exist, or the key has no grant covering the name.

            Error codes: `index:not_found` - The name belongs to no index, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The name belongs to no index, or the key holds no grant covering\
              \ it."
        "409":
          description: |-
            The definition conflicts with documents stored in the generation (`index:definition:incompatible`), the stored definition contains settings this API version cannot represent (`index:definition:unrepresentable`), the index requires engine features this node does not have (`index:unsupported`), a reindex job is already running (`reindex:in_progress`), storage holds a generation under the new name that nothing deleted (`index:generation:storage_held`), no node is available to write the index (`indexer:unavailable`), or the registry write failed.

            Error codes: `index:field:type_unrepresentable` - The stored definition holds a field of a type this API version cannot describe, which a `PUT` would discard. The `name` argument names the field. `index:no_live_generation` - The index has no live generation. Promote one and send the request again. `index:definition:incompatible` - The definition conflicts with documents already stored in the generation. Write the change to a new generation. `index:definition:unrepresentable` - The stored definition holds settings this API version cannot describe. `index:unsupported` - The index needs engine features this node does not have. `reindex:in_progress` - A reindex job is already running for the index. `index:generation:storage_held` - Storage holds a generation under the new name that nothing deleted. Repair the registry, or remove its objects. `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `storage:conflict` - The registry kept being written by other nodes. Send the request again. `index:already_exists` - The index was created by another request while this one was creating it. Send the request again to replace it. `index:generation:already_exists` - The generation was created by another request while this one was creating it. `index:definition:analysis_changed` - The definition changes how a usage reads a field the generation already holds documents for. Carried inside `index:definition:incompatible`. `index:definition:setting_changed` - The definition changes a setting that decides what was written for the documents the generation holds. Carried inside `index:definition:incompatible`. `index:definition:usage_added` - The definition turns on a usage that writes something the documents the generation holds do not have. Carried inside `index:definition:incompatible`. `index:definition:source_added` - The definition starts keeping a copy of each document, which the documents the generation holds were stored without. Carried inside `index:definition:incompatible`. `index:definition:locale_fallback_changed` - The definition changes the locale fallback that decided which locales were filled in for the documents the generation holds. Carried inside `index:definition:incompatible`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:field:type_unrepresentable
            when: "The stored definition holds a field of a type this API version\
              \ cannot describe, which a `PUT` would discard. The `name` argument\
              \ names the field."
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
          - code: index:definition:incompatible
            when: The definition conflicts with documents already stored in the generation.
              Write the change to a new generation.
          - code: index:definition:unrepresentable
            when: The stored definition holds settings this API version cannot describe.
          - code: index:unsupported
            when: The index needs engine features this node does not have.
          - code: reindex:in_progress
            when: A reindex job is already running for the index.
          - code: index:generation:storage_held
            when: "Storage holds a generation under the new name that nothing deleted.\
              \ Repair the registry, or remove its objects."
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: storage:conflict
            when: The registry kept being written by other nodes. Send the request
              again.
          - code: index:already_exists
            when: The index was created by another request while this one was creating
              it. Send the request again to replace it.
          - code: index:generation:already_exists
            when: The generation was created by another request while this one was
              creating it.
          - code: index:definition:analysis_changed
            when: The definition changes how a usage reads a field the generation
              already holds documents for. Carried inside `index:definition:incompatible`.
          - code: index:definition:setting_changed
            when: The definition changes a setting that decides what was written for
              the documents the generation holds. Carried inside `index:definition:incompatible`.
          - code: index:definition:usage_added
            when: The definition turns on a usage that writes something the documents
              the generation holds do not have. Carried inside `index:definition:incompatible`.
          - code: index:definition:source_added
            when: "The definition starts keeping a copy of each document, which the\
              \ documents the generation holds were stored without. Carried inside\
              \ `index:definition:incompatible`."
          - code: index:definition:locale_fallback_changed
            when: The definition changes the locale fallback that decided which locales
              were filled in for the documents the generation holds. Carried inside
              `index:definition:incompatible`.
        "412":
          description: |-
            The `If-Match` version does not match the stored definition. Re-read the index and rebuild the change against the new version.

            Error codes: `index:version_mismatch` - The `If-Match` version is not the one the stored definition is at. Read the index again and rebuild the change.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:version_mismatch
            when: The `If-Match` version is not the one the stored definition is at.
              Read the index again and rebuild the change.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The request raced the index being closed. Retrying the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed. Sending it again reopens
              the index.
      security:
      - apiKey: []
      x-required-permission: indexes.write
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
    get:
      summary: Get an index
      description: |-
        Returns the index resource: its definition as stored, the generation described in the response, every generation it holds, and the status the answering node observes.

        The definition version is returned in the `ETag` header. Pass this value in the `If-Match` header on `PUT` requests to prevent overwriting concurrent updates, or in the `If-None-Match` header on a later `GET` to receive `304 Not Modified` while the definition is still at that version.

        Requires the `indexes.read` permission on the index the path names. The `reader`, `writer` and `admin` roles include it.
      operationId: getIndex
      tags:
      - Indexes
      parameters:
      - description: "The index, which means the generation it answers for, or one\
          \ generation by name such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "Definition versions the client already holds, as `ETag` values\
          \ separated by commas, or `*` for any. While the stored version is one of\
          \ them, the response is `304 Not Modified` with no body."
        example: '"9f2c1a0b3d4e5f60"'
        name: If-None-Match
        in: header
        schema:
          type: string
      responses:
        "200":
          description: "The index, with its version in the `ETag` header. Presets\
            \ are stored expanded; the response returns the expanded chain rather\
            \ than the preset name."
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "304":
          description: The definition is still at a version the `If-None-Match` header
            names. The response carries the version in the `ETag` header and no body.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
        "404":
          description: |-
            No index or generation has this name, or the key has no grant covering it.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The node cannot describe the index. Send the request to a node running a version that supports it.

            Error codes: `index:definition:unrepresentable` - The stored definition holds settings this API version cannot describe. `index:field:type_unrepresentable` - The stored definition holds a field of a type this API version cannot describe. The `name` argument names the field. `index:unsupported` - The index needs engine features this node does not have. `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:definition:unrepresentable
            when: The stored definition holds settings this API version cannot describe.
          - code: index:field:type_unrepresentable
            when: The stored definition holds a field of a type this API version cannot
              describe. The `name` argument names the field.
          - code: index:unsupported
            when: The index needs engine features this node does not have.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The request raced the index being closed. Retrying the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed. Sending it again reopens
              the index.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
    delete:
      summary: Delete an index or a generation
      description: |-
        Deleting `books` deletes the index and all of its generations; deleting `books@2` deletes only that generation. Deleting the live generation fails with `index:generation:is_live` until another generation is promoted.

        Deleting an index or generation removes it from the shared registry across the deployment; other nodes remove their local copies during their next registry read. What remote storage holds - the generations and, for an index, its search settings - is marked as deleted and removed by a background sweep once the mark is older than `EXOFIND_INDEXES_REMOVAL_GRACE`. Until then a registry repair with `restore` brings the index or generation back. An index or generation created again under the same name starts empty, whether or not the sweep has run.

        Served by the node writing the index and forwarded there when another node receives it.

        Requires the `indexes.delete` permission on the index the path names. The `admin` role includes it.
      operationId: deleteIndex
      tags:
      - Indexes
      parameters:
      - description: "The index name, which deletes the index and all of its generations,\
          \ or a specific generation by name such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The index or generation was removed.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.delete` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.delete` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.delete` permission.
        "404":
          description: |-
            No index or generation has this name, or the key has no grant covering it.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The index or generation cannot be removed right now.

            Error codes: `index:generation:is_live` - The generation is the live one. Promote another generation first. `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `storage:conflict` - The registry kept being written by other nodes. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:generation:is_live
            when: The generation is the live one. Promote another generation first.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: storage:conflict
            when: The registry kept being written by other nodes. Send the request
              again.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: indexes.delete
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}/actions/commit:
    post:
      summary: Commit pending changes
      description: |-
        Pushes pending changes (documents and definition) to storage, making them searchable. The index writer commits automatically based on indexing volume or elapsed time. Use this endpoint to commit immediately, such as after loading a dataset.

        Acts on the generation specified in the request path, or the live generation if omitted. Runs on the node that writes the index.

        Requires the `indexes.commit` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: commitIndex
      tags:
      - Indexes
      parameters:
      - description: "The index name, which commits the live generation, or a specific\
          \ generation by name such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: "The index after the commit, in the shape `GET` returns, with\
            \ the definition version in the `ETag` header."
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.commit` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.commit` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.commit` permission.
        "404":
          description: |-
            No index or generation has this name, or the key has no grant covering it.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The index cannot be committed right now.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `index:field:type_unrepresentable` - The stored definition holds a field of a type this API version cannot describe. The `name` argument names the field. `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: index:field:type_unrepresentable
            when: The stored definition holds a field of a type this API version cannot
              describe. The `name` argument names the field.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The request raced the index being closed. Retrying the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed. Sending it again reopens
              the index.
      security:
      - apiKey: []
      x-required-permission: indexes.commit
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}/actions/promote:
    post:
      summary: Promote a generation
      description: |-
        Configures the index to serve from the specified generation. The change takes effect immediately on the receiving node and within `EXOFIND_INDEXES_REFRESH_INTERVAL` on all other nodes. To roll back a deployment, promote the previous generation.

        The request path must specify a generation name; calling `promote` without a generation returns `index:generation:name_required`. Promoting the target of a `ready` reindex job finishes the job, while promoting before the job is ready is refused with `reindex:target_busy`.

        Requires the `indexes.promote` permission on the index the path names. The `admin` role includes it.
      operationId: promoteGeneration
      tags:
      - Indexes
      parameters:
      - description: "The generation to promote, as `index@generation`."
        example: books@2
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The index now answers from this generation.
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "400":
          description: |-
            The path names no generation.

            Error codes: `index:generation:name_required` - The path names an index without a generation. Name one as `index@generation`. `index:generation:name_invalid` - The name in the path names a generation that is not a valid generation name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:generation:name_required
            when: The path names an index without a generation. Name one as `index@generation`.
          - code: index:generation:name_invalid
            when: The name in the path names a generation that is not a valid generation
              name.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.promote` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.promote` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.promote` permission.
        "404":
          description: |-
            No index or generation has this name, or the key has no grant covering it.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The generation cannot be promoted right now.

            Error codes: `reindex:target_busy` - A reindex job is still filling this generation. Promote it once the job is ready. `index:generation:live_moved` - Another generation was promoted while the reindex job that filled this one was running. The job moves to `failed`; start a new job that reads from the generation the `live` argument names. `index:field:type_unrepresentable` - The stored definition holds a field of a type this API version cannot describe. The `name` argument names the field. `index:no_live_generation` - The index has no live generation. Promote one and send the request again. `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `storage:conflict` - The registry kept being written by other nodes. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: reindex:target_busy
            when: A reindex job is still filling this generation. Promote it once
              the job is ready.
          - code: index:generation:live_moved
            when: Another generation was promoted while the reindex job that filled
              this one was running. The job moves to `failed`; start a new job that
              reads from the generation the `live` argument names.
          - code: index:field:type_unrepresentable
            when: The stored definition holds a field of a type this API version cannot
              describe. The `name` argument names the field.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: storage:conflict
            when: The registry kept being written by other nodes. Send the request
              again.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: indexes.promote
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}/actions/pull:
    post:
      summary: Pull the latest state
      description: |-
        Fetches the latest remote state immediately instead of waiting for `EXOFIND_INDEXES_REFRESH_INTERVAL`, and returns the index as this node then sees it.

        A pull updates the local copy on the receiving node and is never forwarded.

        Requires the `indexes.pull` permission on the index the path names. The `admin` role includes it.
      operationId: pullIndex
      tags:
      - Indexes
      parameters:
      - description: "The index name, which pulls the live generation, or a specific\
          \ generation by name such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: "The index after the pull, in the shape `GET` returns, with\
            \ the definition version in the `ETag` header. The status is the one this\
            \ node observes."
          content:
            application/json:
              examples:
                index:
                  value:
                    name: products
                    generation: "2"
                    live: true
                    version: 9f2c1a0b3d4e5f60
                    definition:
                      fields:
                        id:
                          type: string
                          primaryKey: true
                          required: true
                        name:
                          type: string
                          matching: {}
                          sort: {}
                    status:
                      state: usable
                      readOnly: false
                      indexer:
                        node: node-a-7f21
                        address: http://node-a:8080
                      luceneCompatibility: current
                    generations:
                    - name: "1"
                      live: false
                      createdAt: 2026-08-16T11:02:07Z
                    - name: "2"
                      live: true
                      createdAt: 2026-08-28T10:15:30Z
              schema:
                $ref: "#/components/schemas/IndexInfo"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.pull` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.pull` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.pull` permission.
        "404":
          description: |-
            No index or generation has this name, or the key has no grant covering it.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The index cannot be pulled right now.

            Error codes: `index:field:type_unrepresentable` - The stored definition holds a field of a type this API version cannot describe. The `name` argument names the field. `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:field:type_unrepresentable
            when: The stored definition holds a field of a type this API version cannot
              describe. The `name` argument names the field.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The request raced the index being closed. Retrying the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed. Sending it again reopens
              the index.
      security:
      - apiKey: []
      x-required-permission: indexes.pull
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}/actions/reindex:
    post:
      summary: Start a reindex job
      description: |-
        Starts a reindex job that populates a new generation by copying documents from an existing generation of the same index inside the engine. The request returns immediately with the job record; the job runs in the background on the node holding the index.

        The target must specify a generation by name, must already exist, must be empty, and must not be the live generation. The source generation must have a primary key and keep document sources, and the primary keys of source and target must share a field name and type. If the target does not meet these requirements, the server returns `400`.

        The job automatically promotes the target generation once it catches up with changes, unless the request specifies `"promote": "manual"`. With manual promotion, the job pauses in the `ready` phase and keeps the target caught up until `actions/promote` on the target finishes the job.

        Because promoting changes what the index answers for, a request that leaves promotion automatic also needs `indexes.promote` on the target and is refused with `403` without it. A request specifying `"promote": "manual"` needs only `indexes.reindex`.

        An index can run at most one reindex job at a time. A finished job's record remains readable until a new job replaces it. Read the record with `GET /v1alpha1/admin/reindexes/{name}`, which the `Location` header of the response names.

        Requires the `indexes.reindex` permission on the index the path names. The `admin` role includes it.
      operationId: startReindex
      tags:
      - Reindexes
      parameters:
      - description: "The generation to fill, as `index@generation`. It must already\
          \ exist, be empty, and not be the live generation."
        example: products@2
        name: name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            examples:
              job:
                value:
                  from: products@1
                  promote: manual
            schema:
              $ref: "#/components/schemas/ReindexRequest"
        required: false
      responses:
        "202":
          description: A reindex job was started and runs asynchronously. The `Location`
            header names the status endpoint of the job.
          content:
            application/json:
              examples:
                job:
                  value:
                    id: 6f1c2a9d8b3e4c05
                    index: products
                    target: products@2
                    source: products@1
                    phase: pending
                    promote: auto
                    documentsCopied: 0
                    sourceDocuments: 2400000
                    backlog: 0
                    error: null
                    startedBy: 3f9a1c7e2b8d4650
                    node: node-a-7f21
                    startedAt: 2026-08-28T10:15:30Z
                    updatedAt: 2026-08-28T10:15:30Z
                    finishedAt: null
                    freshness: null
              schema:
                $ref: "#/components/schemas/ReindexInfo"
        "400":
          description: |-
            The target does not specify a generation by name, does not exist, is not empty, is the live generation, or the source and target primary keys do not match.

            Error codes: `reindex:target_generation_required` - The target names an index without a generation. Name one as `index@generation`. `reindex:target_is_live` - The target is the live generation. Fill another generation and promote it. `reindex:target_not_empty` - The target generation already holds documents. `reindex:source_is_target` - The source and the target are the same generation. `reindex:source_other_index` - The source belongs to another index. `reindex:primary_key_mismatch` - The source and the target declare different primary keys. `reindex:promote_invalid` - `promote` is neither `auto` nor `manual`. `index:name_invalid` - The path or `from` holds a name that is not a valid index or generation name. `index:no_primary_key` - The source or the target declares no primary key, so documents cannot be matched up between them. `document:source_not_kept` - The source generation keeps no copy of the documents to read them back from. A reindex reads the stored copies.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: reindex:target_generation_required
            when: The target names an index without a generation. Name one as `index@generation`.
          - code: reindex:target_is_live
            when: The target is the live generation. Fill another generation and promote
              it.
          - code: reindex:target_not_empty
            when: The target generation already holds documents.
          - code: reindex:source_is_target
            when: The source and the target are the same generation.
          - code: reindex:source_other_index
            when: The source belongs to another index.
          - code: reindex:primary_key_mismatch
            when: The source and the target declare different primary keys.
          - code: reindex:promote_invalid
            when: '`promote` is neither `auto` nor `manual`.'
          - code: index:name_invalid
            when: The path or `from` holds a name that is not a valid index or generation
              name.
          - code: index:no_primary_key
            when: "The source or the target declares no primary key, so documents\
              \ cannot be matched up between them."
          - code: document:source_not_kept
            when: The source generation keeps no copy of the documents to read them
              back from. A reindex reads the stored copies.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.reindex` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.reindex` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.reindex` permission.
        "404":
          description: |-
            The specified index or generation does not exist, or the caller key lacks permissions on the index.

            Error codes: `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The job could not be started.

            Error codes: `index:no_live_generation` - The index has no live generation to read from. Promote one, or name the source with `from`. `reindex:in_progress` - A reindex job is already running for the index. Wait for it, or cancel it. `reindex:target_busy` - Another job holds the target generation. `storage:io_error` - The record of the reindex could not be written. Send the request again once the storage responds. `indexer:unavailable` - No node is available to write the index. Send the request again once one is.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: "The index has no live generation to read from. Promote one, or\
              \ name the source with `from`."
          - code: reindex:in_progress
            when: "A reindex job is already running for the index. Wait for it, or\
              \ cancel it."
          - code: reindex:target_busy
            when: Another job holds the target generation.
          - code: storage:io_error
            when: The record of the reindex could not be written. Send the request
              again once the storage responds.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
        "502":
          description: |-
            The request was forwarded to the index writer and the writer did not respond.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: indexes.reindex
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/indexes/{name}/settings:
    put:
      summary: Replace search settings
      description: |-
        Replaces the settings completely and returns them as stored, answering `201` while the index had none and `200` while it had. While a `ranking` is present, it replaces the definition's ranking completely; an empty object turns ranking off.

        The server validates the ranking against the generation the index name answers from, using the same `index:ranking:*` error codes used to validate a definition's ranking. The server validates the fields named by `synonyms`, `typoExclusions`, and `fields` against the same generation.

        A change takes effect for searches on the answering node immediately and on all other nodes within `EXOFIND_SETTINGS_REFRESH_INTERVAL`. Until then, two nodes can rank the same query differently. Search settings outlive generations: a generation promoted later can lack a field the settings name, and searches then skip that entry rather than fail.

        Runs on the node that writes the index. The `settings.write` permission is separate from `indexes.write`, so relevance tuning can be granted without the power to change what an index contains.

        Requires the `settings.write` permission on the index the path names. The `admin` role includes it.
      operationId: putSearchSettings
      tags:
      - Search settings
      parameters:
      - description: The index the settings belong to. Naming a generation stores
          the same settings and only says which generation to validate the ranking
          against.
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "Version the settings are expected to be at, as returned by a\
          \ previous response's `ETag`. `*` asks only that the index has settings.\
          \ Several versions may be given, separated by commas, and the header is\
          \ satisfied while the stored version is one of them; versions are compared\
          \ exactly, so a weak tag (`W/\"...\"`) matches none. An index with no settings\
          \ answers `404`, and a version that no longer matches answers `412` instead\
          \ of overwriting the change that moved it."
        example: '"9f2c1a0b3d4e5f60"'
        name: If-Match
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            examples:
              settings:
                summary: "A ranking signal, a tie-breaker and a synonym set"
                value:
                  ranking:
                    signals:
                    - field: purchases
                      saturation:
                        pivot: 50
                      weight: 0.5
                    tieBreakers:
                    - field: sales
                      direction: descending
                  synonyms:
                    products:
                      rules:
                      - equivalent:
                        - laptop
                        - notebook
                      fields:
                      - name
                  fields:
                    brand:
                      interpret: {}
                    size:
                      values:
                      - value: S
                        order: 1
                        labels:
                          en: Small
                          sv: Liten
                      - value: M
                        order: 2
                        labels:
                          en: Medium
                          sv: Mellan
                      - value: L
                        order: 3
                        labels:
                          en: Large
                          sv: Stor
            schema:
              $ref: "#/components/schemas/SearchSettingsDefinition"
        required: true
      responses:
        "200":
          description: Settings that were already stored were replaced. The new version
            is in the `ETag` header.
          content:
            application/json:
              examples:
                settings:
                  value:
                    ranking:
                      signals:
                      - field: purchases
                        saturation:
                          pivot: 50
                        weight: 0.5
                      tieBreakers:
                      - field: sales
                        direction: descending
                    version: 9f2c1a0b3d4e5f60
              schema:
                $ref: "#/components/schemas/SearchSettingsInfo"
        "201":
          description: "The index had no settings, so these are its first. The version\
            \ is in the `ETag` header."
          content:
            application/json:
              examples:
                settings:
                  value:
                    ranking:
                      signals:
                      - field: purchases
                        saturation:
                          pivot: 50
                        weight: 0.5
                      tieBreakers:
                      - field: sales
                        direction: descending
                    version: 9f2c1a0b3d4e5f60
              schema:
                $ref: "#/components/schemas/SearchSettingsInfo"
        "400":
          description: |-
            The request body is missing, or the settings failed validation against the generation the index answers from.

            Error codes: `request:body_required` - The request carries no body. `request:value_required` - A property of the settings that needs a value is `null`. `settings:synonyms:field_unknown` - A synonym set names a field the generation does not have. `settings:synonyms:field_unsupported` - A synonym set names a field that is not searched as text. `settings:synonyms:boost_out_of_range` - The boost of a synonym set is not a positive number. `settings:synonyms:rule_invalid` - A synonym rule is not exactly one kind - equivalent words, or a one-way mapping. `settings:typo_exclusions:field_unknown` - A typo exclusion names a field the generation does not have. `settings:typo_exclusions:field_unsupported` - A typo exclusion names a field that is not searched as text. `settings:fields:field_unknown` - The field settings name a field the generation does not have. `settings:fields:interpret_unsupported` - The settings read the values of a field that is not a `string` field with `filter` and `facet` and without `hierarchy`. `settings:fields:values_unsupported` - The settings declare values of a field that is not a `string` field with `facet` and without `hierarchy`. `settings:fields:values_invalid` - A declared value carries no `value`, repeats one, is labelled under a tag that is not canonical BCP 47, holds a blank label, or the field declares more than 10000 values. `settings:fields:suggest_unsupported` - The settings suggest the values of a field that is not a `string` field with `facet` and without `hierarchy`. `index:ranking:field_not_sortable` - A ranking signal names a field that is not sortable. `index:ranking:field_unknown` - A tie-breaker names a field the generation does not have. `index:ranking:wildcard_unsupported` - A tie-breaker names fields with a wildcard. A tie-breaker orders by one field. `index:ranking:field_duplicate` - Two tie-breakers name the same field. `index:ranking:signal:field_unknown` - A ranking signal names a field the generation does not have. `index:ranking:signal:wildcard_unsupported` - A ranking signal names fields with a wildcard. A signal reads one field. `index:ranking:signal:field_not_sortable` - A ranking signal names a field that holds no value to read. `index:ranking:signal:shape_required` - A ranking signal says no shape to read its field with. `index:ranking:signal:shape_unsupported` - A ranking signal reads its field with a shape that the type of the field has no meaning for. `index:ranking:signal:shape_invalid` - A ranking signal is not exactly one of `saturation`, `decay` and `linear`. `index:ranking:signal:pivot_out_of_range` - The `pivot` of a saturation signal is not a number above zero. `index:ranking:signal:half_life_out_of_range` - The `halfLife` of a decay signal is not a number of seconds above zero. `index:ranking:signal:ceiling_out_of_range` - The `ceiling` of a linear signal is not a number above zero. `index:ranking:signal:weight_out_of_range` - The `weight` of a ranking signal is below zero or is not a finite number.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:body_required
            when: The request carries no body.
          - code: request:value_required
            when: A property of the settings that needs a value is `null`.
          - code: settings:synonyms:field_unknown
            when: A synonym set names a field the generation does not have.
          - code: settings:synonyms:field_unsupported
            when: A synonym set names a field that is not searched as text.
          - code: settings:synonyms:boost_out_of_range
            when: The boost of a synonym set is not a positive number.
          - code: settings:synonyms:rule_invalid
            when: "A synonym rule is not exactly one kind - equivalent words, or a\
              \ one-way mapping."
          - code: settings:typo_exclusions:field_unknown
            when: A typo exclusion names a field the generation does not have.
          - code: settings:typo_exclusions:field_unsupported
            when: A typo exclusion names a field that is not searched as text.
          - code: settings:fields:field_unknown
            when: The field settings name a field the generation does not have.
          - code: settings:fields:interpret_unsupported
            when: The settings read the values of a field that is not a `string` field
              with `filter` and `facet` and without `hierarchy`.
          - code: settings:fields:values_unsupported
            when: The settings declare values of a field that is not a `string` field
              with `facet` and without `hierarchy`.
          - code: settings:fields:values_invalid
            when: "A declared value carries no `value`, repeats one, is labelled under\
              \ a tag that is not canonical BCP 47, holds a blank label, or the field\
              \ declares more than 10000 values."
          - code: settings:fields:suggest_unsupported
            when: The settings suggest the values of a field that is not a `string`
              field with `facet` and without `hierarchy`.
          - code: index:ranking:field_not_sortable
            when: A ranking signal names a field that is not sortable.
          - code: index:ranking:field_unknown
            when: A tie-breaker names a field the generation does not have.
          - code: index:ranking:wildcard_unsupported
            when: A tie-breaker names fields with a wildcard. A tie-breaker orders
              by one field.
          - code: index:ranking:field_duplicate
            when: Two tie-breakers name the same field.
          - code: index:ranking:signal:field_unknown
            when: A ranking signal names a field the generation does not have.
          - code: index:ranking:signal:wildcard_unsupported
            when: A ranking signal names fields with a wildcard. A signal reads one
              field.
          - code: index:ranking:signal:field_not_sortable
            when: A ranking signal names a field that holds no value to read.
          - code: index:ranking:signal:shape_required
            when: A ranking signal says no shape to read its field with.
          - code: index:ranking:signal:shape_unsupported
            when: A ranking signal reads its field with a shape that the type of the
              field has no meaning for.
          - code: index:ranking:signal:shape_invalid
            when: "A ranking signal is not exactly one of `saturation`, `decay` and\
              \ `linear`."
          - code: index:ranking:signal:pivot_out_of_range
            when: The `pivot` of a saturation signal is not a number above zero.
          - code: index:ranking:signal:half_life_out_of_range
            when: The `halfLife` of a decay signal is not a number of seconds above
              zero.
          - code: index:ranking:signal:ceiling_out_of_range
            when: The `ceiling` of a linear signal is not a number above zero.
          - code: index:ranking:signal:weight_out_of_range
            when: The `weight` of a ranking signal is below zero or is not a finite
              number.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `settings.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `settings.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `settings.write` permission.
        "404":
          description: |-
            No index has this name, the API key lacks permissions covering it, or an `If-Match` header was sent for an index that has no settings (`settings:not_found`).

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it. `settings:not_found` - An `If-Match` header was sent and the index has no settings for it to match.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
          - code: settings:not_found
            when: An `If-Match` header was sent and the index has no settings for
              it to match.
        "409":
          description: |-
            The settings could not be stored. The stored settings remain unchanged; send the request again.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again. `storage:conflict` - Other writers kept changing the settings. The stored settings are unchanged; send the request again. `storage:io_error` - Settings storage answered with an error. Send the request again. `storage:unavailable` - Settings storage could not be reached. Send the request again once it answers. `indexer:unavailable` - No node is available to write the index. Send the request again once one is.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
          - code: storage:conflict
            when: Other writers kept changing the settings. The stored settings are
              unchanged; send the request again.
          - code: storage:io_error
            when: Settings storage answered with an error. Send the request again.
          - code: storage:unavailable
            when: Settings storage could not be reached. Send the request again once
              it answers.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
        "412":
          description: |-
            The `If-Match` version does not match the stored settings. Read them again and rebuild the change on the version that comes back.

            Error codes: `settings:version_mismatch` - The `If-Match` version is not the one the stored settings are at. Read them again and rebuild the change.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: settings:version_mismatch
            when: The `If-Match` version is not the one the stored settings are at.
              Read them again and rebuild the change.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: settings.write
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
    patch:
      summary: Change some of the search settings
      description: |-
        Changes named parts of the settings, leaving the rest as they are, and returns them as stored. The request body is a change object where each key is a path naming a location in the settings: a path with a value replaces the target value, a path set to `null` clears the target value, and an omitted path leaves the existing value unchanged.

        A path is written the same way as a path into a document: names joined by `.`, where a name can carry a bracket selector that picks list entries by what they hold. `ranking.signals[field=sales].weight` changes one weight, `ranking.signals[field=sales]` replaces one signal, `ranking.signals[]` adds a signal, and `ranking` replaces or clears the whole ranking. A selector picks entries by content, so a change still names the same entry after the list is reordered.

        A backslash escapes the character after it, so a name can hold a `.`, a `[` or a backslash of its own: `fields.variants\.colour.interpret` names the field `variants.colour`. In JSON, write each backslash twice.

        A selector holding a single word names an entry by the key its list declares: `ranking.signals[sales]` names the signals reading the field `sales`, `ranking.tieBreakers[sales]` the tie breaker on it, and `fields.size.values[S]` the declared value `S`. Two signals may read one field with different shapes, so a key of `ranking.signals` names every signal reading it, where a key of `ranking.tieBreakers` and of `fields.<name>.values` names at most one. A word on a list that declares no key, such as a synonym rule, returns `settings:patch:key_unsupported`. For the whole syntax, see [Change paths](https://exofind.dev/reference/patch-paths/).

        The merged settings are validated against the generation the index name answers from, using the same `index:ranking:*` error codes as a `PUT` request. An index with no stored settings is modified as if it had empty settings, and the answer is `201` rather than `200` because the change stores its first ones.

        Without an `If-Match` header, a change that conflicts with a concurrent update rebuilds on the newer version up to three times before returning `storage:conflict`. With an `If-Match` header naming versions, a version mismatch returns `412` without retrying, and an index with no settings returns `404`.

        Takes effect immediately on the answering node and on all other nodes within `EXOFIND_SETTINGS_REFRESH_INTERVAL`.

        Runs on the node that writes the index.

        Requires the `settings.write` permission on the index the path names. The `admin` role includes it.
      operationId: patchSearchSettings
      tags:
      - Search settings
      parameters:
      - description: The index the settings belong to. Naming a generation stores
          the same settings and only says which generation to validate the ranking
          against.
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "Version the settings are expected to be at, as returned by a\
          \ previous response's `ETag`. `*` asks only that the index has settings.\
          \ Several versions may be given, separated by commas, and the header is\
          \ satisfied while the stored version is one of them; versions are compared\
          \ exactly, so a weak tag (`W/\"...\"`) matches none. An index with no settings\
          \ answers `404`, and a version that no longer matches answers `412` instead\
          \ of building the change on the one that replaced it."
        example: '"9f2c1a0b3d4e5f60"'
        name: If-Match
        in: header
        schema:
          type: string
      requestBody:
        description: "The places to change, keyed by path. A path with a value replaces\
          \ what it names, a path set to `null` clears it, and a place no path names\
          \ is left as it is."
        content:
          application/json:
            examples:
              change:
                value:
                  ranking.signals[field=sales].weight: 2.0
            schema:
              type: object
        required: true
      responses:
        "200":
          description: "The settings as stored, with their new version in the `ETag`\
            \ header."
          content:
            application/json:
              examples:
                settings:
                  value:
                    ranking:
                      signals:
                      - field: purchases
                        saturation:
                          pivot: 50
                        weight: 0.5
                      tieBreakers:
                      - field: sales
                        direction: descending
                    version: 9f2c1a0b3d4e5f60
              schema:
                $ref: "#/components/schemas/SearchSettingsInfo"
        "201":
          description: "The index had no settings, so the change stored its first\
            \ ones. The version is in the `ETag` header."
          content:
            application/json:
              examples:
                settings:
                  value:
                    ranking:
                      signals:
                      - field: purchases
                        saturation:
                          pivot: 50
                        weight: 0.5
                      tieBreakers:
                      - field: sales
                        direction: descending
                    version: 9f2c1a0b3d4e5f60
              schema:
                $ref: "#/components/schemas/SearchSettingsInfo"
        "400":
          description: |-
            The request body is missing, a key names a place the settings cannot be changed at, or the result failed validation against the generation the index answers from.

            Error codes: `request:body_required` - The request carries no body. `settings:patch:path_invalid` - A key of the change could not be read as a path. `settings:patch:no_match` - A selector names nothing the settings hold. A selector never creates the value it names. `settings:patch:value_invalid` - A key names a field that cannot hold the given value. `settings:patch:field_unknown` - A key reaches into a field the search settings do not have. `settings:patch:not_an_object` - A key reaches inside a field that holds no fields. `settings:patch:selector_required` - A key names a field that holds a list without saying which value, such as `ranking.signals[field=sales]`. `settings:patch:selector_unsupported` - A key names one value of a field that holds no list. `settings:patch:add_reaches_inside` - A key reaches inside a value that the same change adds, which does not exist yet. Give the whole value instead. `settings:patch:add_unsupported` - A key adds a value to a field that holds a single value, such as `ranking[]`. Name the field on its own to replace it. `settings:patch:key_unsupported` - A key names a list entry by a single word on a list that declares no key, such as `synonyms.<name>.rules[x]`. Name a field inside the entry instead, as `rules[field=value]`. `settings:patch:match_not_an_object` - A key matches on a field inside the entries of a list whose entries are not objects. `request:value_required` - A property of the settings that needs a value is `null`. `settings:synonyms:field_unknown` - A synonym set names a field the generation does not have. `settings:synonyms:field_unsupported` - A synonym set names a field that is not searched as text. `settings:synonyms:boost_out_of_range` - The boost of a synonym set is not a positive number. `settings:synonyms:rule_invalid` - A synonym rule is not exactly one kind - equivalent words, or a one-way mapping. `settings:typo_exclusions:field_unknown` - A typo exclusion names a field the generation does not have. `settings:typo_exclusions:field_unsupported` - A typo exclusion names a field that is not searched as text. `settings:fields:field_unknown` - The field settings name a field the generation does not have. `settings:fields:interpret_unsupported` - The settings read the values of a field that is not a `string` field with `filter` and `facet` and without `hierarchy`. `settings:fields:values_unsupported` - The settings declare values of a field that is not a `string` field with `facet` and without `hierarchy`. `settings:fields:values_invalid` - A declared value carries no `value`, repeats one, is labelled under a tag that is not canonical BCP 47, holds a blank label, or the field declares more than 10000 values. `settings:fields:suggest_unsupported` - The settings suggest the values of a field that is not a `string` field with `facet` and without `hierarchy`. `index:ranking:field_not_sortable` - A ranking signal names a field that is not sortable. `index:ranking:field_unknown` - A tie-breaker names a field the generation does not have. `index:ranking:wildcard_unsupported` - A tie-breaker names fields with a wildcard. A tie-breaker orders by one field. `index:ranking:field_duplicate` - Two tie-breakers name the same field. `index:ranking:signal:field_unknown` - A ranking signal names a field the generation does not have. `index:ranking:signal:wildcard_unsupported` - A ranking signal names fields with a wildcard. A signal reads one field. `index:ranking:signal:field_not_sortable` - A ranking signal names a field that holds no value to read. `index:ranking:signal:shape_required` - A ranking signal says no shape to read its field with. `index:ranking:signal:shape_unsupported` - A ranking signal reads its field with a shape that the type of the field has no meaning for. `index:ranking:signal:shape_invalid` - A ranking signal is not exactly one of `saturation`, `decay` and `linear`. `index:ranking:signal:pivot_out_of_range` - The `pivot` of a saturation signal is not a number above zero. `index:ranking:signal:half_life_out_of_range` - The `halfLife` of a decay signal is not a number of seconds above zero. `index:ranking:signal:ceiling_out_of_range` - The `ceiling` of a linear signal is not a number above zero. `index:ranking:signal:weight_out_of_range` - The `weight` of a ranking signal is below zero or is not a finite number.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:body_required
            when: The request carries no body.
          - code: settings:patch:path_invalid
            when: A key of the change could not be read as a path.
          - code: settings:patch:no_match
            when: A selector names nothing the settings hold. A selector never creates
              the value it names.
          - code: settings:patch:value_invalid
            when: A key names a field that cannot hold the given value.
          - code: settings:patch:field_unknown
            when: A key reaches into a field the search settings do not have.
          - code: settings:patch:not_an_object
            when: A key reaches inside a field that holds no fields.
          - code: settings:patch:selector_required
            when: "A key names a field that holds a list without saying which value,\
              \ such as `ranking.signals[field=sales]`."
          - code: settings:patch:selector_unsupported
            when: A key names one value of a field that holds no list.
          - code: settings:patch:add_reaches_inside
            when: "A key reaches inside a value that the same change adds, which does\
              \ not exist yet. Give the whole value instead."
          - code: settings:patch:add_unsupported
            when: "A key adds a value to a field that holds a single value, such as\
              \ `ranking[]`. Name the field on its own to replace it."
          - code: settings:patch:key_unsupported
            when: "A key names a list entry by a single word on a list that declares\
              \ no key, such as `synonyms.<name>.rules[x]`. Name a field inside the\
              \ entry instead, as `rules[field=value]`."
          - code: settings:patch:match_not_an_object
            when: A key matches on a field inside the entries of a list whose entries
              are not objects.
          - code: request:value_required
            when: A property of the settings that needs a value is `null`.
          - code: settings:synonyms:field_unknown
            when: A synonym set names a field the generation does not have.
          - code: settings:synonyms:field_unsupported
            when: A synonym set names a field that is not searched as text.
          - code: settings:synonyms:boost_out_of_range
            when: The boost of a synonym set is not a positive number.
          - code: settings:synonyms:rule_invalid
            when: "A synonym rule is not exactly one kind - equivalent words, or a\
              \ one-way mapping."
          - code: settings:typo_exclusions:field_unknown
            when: A typo exclusion names a field the generation does not have.
          - code: settings:typo_exclusions:field_unsupported
            when: A typo exclusion names a field that is not searched as text.
          - code: settings:fields:field_unknown
            when: The field settings name a field the generation does not have.
          - code: settings:fields:interpret_unsupported
            when: The settings read the values of a field that is not a `string` field
              with `filter` and `facet` and without `hierarchy`.
          - code: settings:fields:values_unsupported
            when: The settings declare values of a field that is not a `string` field
              with `facet` and without `hierarchy`.
          - code: settings:fields:values_invalid
            when: "A declared value carries no `value`, repeats one, is labelled under\
              \ a tag that is not canonical BCP 47, holds a blank label, or the field\
              \ declares more than 10000 values."
          - code: settings:fields:suggest_unsupported
            when: The settings suggest the values of a field that is not a `string`
              field with `facet` and without `hierarchy`.
          - code: index:ranking:field_not_sortable
            when: A ranking signal names a field that is not sortable.
          - code: index:ranking:field_unknown
            when: A tie-breaker names a field the generation does not have.
          - code: index:ranking:wildcard_unsupported
            when: A tie-breaker names fields with a wildcard. A tie-breaker orders
              by one field.
          - code: index:ranking:field_duplicate
            when: Two tie-breakers name the same field.
          - code: index:ranking:signal:field_unknown
            when: A ranking signal names a field the generation does not have.
          - code: index:ranking:signal:wildcard_unsupported
            when: A ranking signal names fields with a wildcard. A signal reads one
              field.
          - code: index:ranking:signal:field_not_sortable
            when: A ranking signal names a field that holds no value to read.
          - code: index:ranking:signal:shape_required
            when: A ranking signal says no shape to read its field with.
          - code: index:ranking:signal:shape_unsupported
            when: A ranking signal reads its field with a shape that the type of the
              field has no meaning for.
          - code: index:ranking:signal:shape_invalid
            when: "A ranking signal is not exactly one of `saturation`, `decay` and\
              \ `linear`."
          - code: index:ranking:signal:pivot_out_of_range
            when: The `pivot` of a saturation signal is not a number above zero.
          - code: index:ranking:signal:half_life_out_of_range
            when: The `halfLife` of a decay signal is not a number of seconds above
              zero.
          - code: index:ranking:signal:ceiling_out_of_range
            when: The `ceiling` of a linear signal is not a number above zero.
          - code: index:ranking:signal:weight_out_of_range
            when: The `weight` of a ranking signal is below zero or is not a finite
              number.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `settings.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `settings.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `settings.write` permission.
        "404":
          description: |-
            No index has this name, the API key lacks permissions covering it, or an `If-Match` header was sent for an index that has no settings (`settings:not_found`).

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it. `settings:not_found` - An `If-Match` header was sent and the index has no settings for it to match.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
          - code: settings:not_found
            when: An `If-Match` header was sent and the index has no settings for
              it to match.
        "409":
          description: |-
            The change could not be made. The stored settings are unchanged.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again. `storage:conflict` - Other writers kept changing the settings. The stored settings are unchanged; send the request again. `settings:unrepresentable` - The stored settings hold parts this node cannot describe. Send the request to a node that supports them, or replace the settings with a `PUT`. `storage:io_error` - Settings storage answered with an error. Send the request again. `storage:unavailable` - Settings storage could not be reached. Send the request again once it answers. `indexer:unavailable` - No node is available to write the index. Send the request again once one is.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
          - code: storage:conflict
            when: Other writers kept changing the settings. The stored settings are
              unchanged; send the request again.
          - code: settings:unrepresentable
            when: "The stored settings hold parts this node cannot describe. Send\
              \ the request to a node that supports them, or replace the settings\
              \ with a `PUT`."
          - code: storage:io_error
            when: Settings storage answered with an error. Send the request again.
          - code: storage:unavailable
            when: Settings storage could not be reached. Send the request again once
              it answers.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
        "412":
          description: |-
            The `If-Match` version does not match the stored settings (`settings:version_mismatch`). Read them again and rebuild the change on the version that comes back.

            Error codes: `settings:version_mismatch` - The `If-Match` version is not the one the stored settings are at. Read them again and rebuild the change.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: settings:version_mismatch
            when: The `If-Match` version is not the one the stored settings are at.
              Read them again and rebuild the change.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: settings.write
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
    get:
      summary: Get search settings
      description: |-
        Returns the search settings as stored, with their version in the `ETag` header. Settings are read directly from storage rather than from the node's local copy. A request whose `If-None-Match` header names the stored version is answered `304 Not Modified`.

        An index with no search settings - one searching with its definition alone - returns `404` with `settings:not_found` rather than an empty object, ensuring the `ETag` always represents an explicit stored version.

        Served by whichever node receives the request.

        Requires the `indexes.read` permission on the index the path names. The `reader`, `writer` and `admin` roles include it.
      operationId: getSearchSettings
      tags:
      - Search settings
      parameters:
      - description: "The index the settings belong to. Naming a generation reads\
          \ the same settings, as they belong to the index name rather than to a generation."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "Settings versions the client already holds, as `ETag` values\
          \ separated by commas, or `*` for any. While the stored version is one of\
          \ them, the response is `304 Not Modified` with no body."
        example: '"3b7e9d1c5a2f4e60"'
        name: If-None-Match
        in: header
        schema:
          type: string
      responses:
        "200":
          description: "The stored settings, with their version in the `ETag` header."
          content:
            application/json:
              examples:
                settings:
                  value:
                    ranking:
                      signals:
                      - field: purchases
                        saturation:
                          pivot: 50
                        weight: 0.5
                      tieBreakers:
                      - field: sales
                        direction: descending
                    version: 9f2c1a0b3d4e5f60
              schema:
                $ref: "#/components/schemas/SearchSettingsInfo"
        "304":
          description: The settings are still at a version the `If-None-Match` header
            names. The response carries the version in the `ETag` header and no body.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
        "404":
          description: |-
            The index has no search settings (`settings:not_found`), the index does not exist, or the API key lacks permission on the index.

            Error codes: `settings:not_found` - The index has no search settings stored. `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: settings:not_found
            when: The index has no search settings stored.
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            Settings storage could not be reached.

            Error codes: `storage:io_error` - Settings storage answered with an error. Send the request again. `storage:unavailable` - Settings storage could not be reached. Send the request again once it answers. `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:io_error
            when: Settings storage answered with an error. Send the request again.
          - code: storage:unavailable
            when: Settings storage could not be reached. Send the request again once
              it answers.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
    delete:
      summary: Remove search settings
      description: |-
        Removes the settings, returning the index to the ranking in its definition. Takes effect immediately on the node that holds the index and on other nodes within the settings refresh interval. Deleting settings that do not exist changes nothing and answers `204` all the same, so the request can be repeated.

        An index created again under the same name starts without settings: creating it clears everything stored under the name, the settings with it.

        Runs on the node that writes the index.

        Requires the `settings.write` permission on the index the path names. The `admin` role includes it.
      operationId: deleteSearchSettings
      tags:
      - Search settings
      parameters:
      - description: The index whose settings to remove.
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: "The index has no search settings any more, whether or not\
            \ it had any."
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `settings.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `settings.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `settings.write` permission.
        "404":
          description: |-
            No index has this name, or the API key lacks permissions covering it.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The change could not be stored. The stored settings are unchanged.

            Error codes: `storage:conflict` - Other writers kept changing the settings. The stored settings are unchanged; send the request again. `storage:io_error` - Settings storage answered with an error. Send the request again. `storage:unavailable` - Settings storage could not be reached. Send the request again once it answers. `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:conflict
            when: Other writers kept changing the settings. The stored settings are
              unchanged; send the request again.
          - code: storage:io_error
            when: Settings storage answered with an error. Send the request again.
          - code: storage:unavailable
            when: Settings storage could not be reached. Send the request again once
              it answers.
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "502":
          description: |-
            The index writer did not respond to the forwarded request.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: settings.write
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/keys:
    get:
      summary: List API keys
      description: |-
        Lists deployment keys shared across all nodes, along with the local key configuration of the answering node.

        Key credentials are stored only as hashes and cannot be recovered from listings. A lost credential must be replaced.

        `prefix` keeps the keys whose ID starts with it. `limit` caps the answer, and a listing cut short names the last ID in `next`; pass it as `after` to read on. See [Listings](https://exofind.dev/reference/admin-api/#listings).

        Served by whichever node receives the request.

        Requires the `keys.read` permission, which is not about one index. The `admin` role includes it.
      operationId: listKeys
      tags:
      - API keys
      parameters:
      - description: "The ID to continue after, as the `next` field of the previous\
          \ response gave it. The named key is not included."
        example: 4ff6b760264c1918
        name: after
        in: query
        schema:
          type: string
      - description: "Most keys to answer. Without it the whole listing is answered.\
          \ When more remain, the response carries the last ID in `next`."
        schema:
          maximum: 1000
          minimum: 1
          type: integer
        name: limit
        in: query
      - description: Keeps only the keys whose ID starts with this text.
        name: prefix
        in: query
        schema:
          type: string
      responses:
        "200":
          description: The deployment keys and this node's key configuration.
          content:
            application/json:
              examples:
                keys:
                  value:
                    keys:
                    - id: 4ff6b760264c1918
                      description: the search backend
                      grants:
                      - permissions:
                        - indexes.read
                        - search
                        indexes:
                        - products
                      createdAt: 2026-08-16T12:09:33.198275Z
                      expiresAt: null
                    rootKeyConfigured: true
                    anonymousKey: null
              schema:
                $ref: "#/components/schemas/KeyListResponse"
        "400":
          description: |-
            The `limit` parameter is out of range.

            Error codes: `request:limit_out_of_range` - The `limit` parameter is not a whole number from 1 to 1000.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:limit_out_of_range
            when: The `limit` parameter is not a whole number from 1 to 1000.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `keys.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `keys.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `keys.read` permission.
        "409":
          description: |-
            Key storage is unavailable on this node, or could not be reached.

            Error codes: `storage:unavailable` - The node is not configured with key storage. `storage:io_error` - Key storage answered with an error. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:unavailable
            when: The node is not configured with key storage.
          - code: storage:io_error
            when: Key storage answered with an error. Send the request again.
      security:
      - apiKey: []
      x-required-permission: keys.read
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
    post:
      summary: Create an API key
      description: |-
        Creates an API key and returns the generated credential string and key metadata. The full secret credential is returned only in this response because credentials are stored only as hashes. A lost credential cannot be recovered and must be replaced. Server logs record the key `id`, never the credential value.

        When a key is created, roles are expanded into their constituent permissions. Only the resulting permissions are stored in the key. Existing keys do not change permissions if role definitions change in later software versions.

        A key created on one node works on all nodes immediately because nodes look up unseen keys without delay. Served by whichever node receives the request.

        Requires the `keys.write` permission, which is not about one index. The `admin` role includes it.
      operationId: createKey
      tags:
      - API keys
      requestBody:
        content:
          application/json:
            examples:
              key:
                summary: "A reader of one index, expiring at a date"
                value:
                  description: the search backend
                  grants:
                  - role: reader
                    indexes:
                    - products
                  expiresAt: 2027-01-01T00:00:00Z
            schema:
              $ref: "#/components/schemas/KeyDefinition"
        required: true
      responses:
        "201":
          description: The key was created. The `credential` in this response is the
            only copy returned.
          content:
            application/json:
              examples:
                created:
                  value:
                    credential: exok_4ff6b760264c1918_ePQcdT1O9HSATZoXfDbT8hhHGsP9VpZH
                    key:
                      id: 4ff6b760264c1918
                      description: the search backend
                      grants:
                      - permissions:
                        - indexes.read
                        - search
                        indexes:
                        - products
                      createdAt: 2026-08-16T12:09:33.198275Z
                      expiresAt: 2027-01-01T00:00:00Z
              schema:
                $ref: "#/components/schemas/CreatedKey"
        "400":
          description: |-
            The request body is missing, or the key definition failed validation. All validation errors are reported.

            Error codes: `request:body_required` - The request carries no body. `auth:key:role_unknown` - The definition names a role this version does not have. `auth:key:permission_unknown` - The definition names a permission this version does not have. `auth:key:grants_required` - The definition holds no grant, so the key could do nothing. `auth:key:permissions_required` - A grant names neither a role nor a list of permissions. `auth:key:indexes_required` - A grant holds a permission that is about one index and does not say which indexes it covers. `auth:key:index_pattern_invalid` - An entry of `indexes` is neither an index name nor a prefix followed by `*`. `auth:key:indexes_unsupported` - A grant names `indexes` and holds no permission that is about one index, so the patterns would narrow nothing. `auth:key:expiry_invalid` - `expiresAt` is not an ISO 8601 timestamp. `auth:key:expiry_in_past` - `expiresAt` has already passed, so the key would be lapsed from the moment it was created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:body_required
            when: The request carries no body.
          - code: auth:key:role_unknown
            when: The definition names a role this version does not have.
          - code: auth:key:permission_unknown
            when: The definition names a permission this version does not have.
          - code: auth:key:grants_required
            when: "The definition holds no grant, so the key could do nothing."
          - code: auth:key:permissions_required
            when: A grant names neither a role nor a list of permissions.
          - code: auth:key:indexes_required
            when: A grant holds a permission that is about one index and does not
              say which indexes it covers.
          - code: auth:key:index_pattern_invalid
            when: An entry of `indexes` is neither an index name nor a prefix followed
              by `*`.
          - code: auth:key:indexes_unsupported
            when: "A grant names `indexes` and holds no permission that is about one\
              \ index, so the patterns would narrow nothing."
          - code: auth:key:expiry_invalid
            when: '`expiresAt` is not an ISO 8601 timestamp.'
          - code: auth:key:expiry_in_past
            when: "`expiresAt` has already passed, so the key would be lapsed from\
              \ the moment it was created."
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `keys.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `keys.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `keys.write` permission.
        "409":
          description: |-
            The key could not be stored. The stored keys are unchanged.

            Error codes: `storage:unavailable` - The node is not configured with key storage. `storage:io_error` - Key storage answered with an error. Send the request again. `storage:conflict` - Other nodes kept changing the stored keys. The stored keys are unchanged; send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:unavailable
            when: The node is not configured with key storage.
          - code: storage:io_error
            when: Key storage answered with an error. Send the request again.
          - code: storage:conflict
            when: Other nodes kept changing the stored keys. The stored keys are unchanged;
              send the request again.
      security:
      - apiKey: []
      x-required-permission: keys.write
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/keys/{id}:
    delete:
      summary: Revoke an API key
      description: |-
        Revokes an API key. Revocation takes effect on the answering node immediately and across all other nodes within `EXOFIND_AUTH_REFRESH_INTERVAL`, as nodes accept cached keys until their next storage read.

        What a key allows cannot be changed. To change permissions, create a replacement key, migrate clients to the new key, and revoke the old key. To replace only the credential, keeping everything the key allows, use `actions/rotate`. The root key is not stored in key storage and cannot be revoked through the API.

        The answering node refuses to revoke two keys, because it would not start again without them: the last key granted `keys.write` when the node has no root key, and the key named by `EXOFIND_AUTH_ANONYMOUS_KEY`. Create a replacement, or point the configuration elsewhere, and send the request again.

        Served by whichever node receives the request.

        Requires the `keys.write` permission, which is not about one index. The `admin` role includes it.
      operationId: revokeKey
      tags:
      - API keys
      parameters:
      - description: ID of the key to revoke.
        example: 4ff6b760264c1918
        name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: The key was revoked.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `keys.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `keys.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `keys.write` permission.
        "404":
          description: |-
            No key has this ID.

            Error codes: `auth:key:not_found` - No key is stored under this ID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:key:not_found
            when: No key is stored under this ID.
        "409":
          description: |-
            The key could not be revoked. The stored keys are unchanged.

            Error codes: `auth:key:last_administrator` - The key is the last one granted `keys.write` and the node has no root key, so revoking it would leave nobody able to create another. `auth:key:in_use_as_anonymous` - `EXOFIND_AUTH_ANONYMOUS_KEY` names this key on the answering node, so revoking it would stop that node from starting. `storage:unavailable` - The node is not configured with key storage. `storage:io_error` - Key storage answered with an error. Send the request again. `storage:conflict` - Other nodes kept changing the stored keys. The stored keys are unchanged; send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:key:last_administrator
            when: "The key is the last one granted `keys.write` and the node has no\
              \ root key, so revoking it would leave nobody able to create another."
          - code: auth:key:in_use_as_anonymous
            when: "`EXOFIND_AUTH_ANONYMOUS_KEY` names this key on the answering node,\
              \ so revoking it would stop that node from starting."
          - code: storage:unavailable
            when: The node is not configured with key storage.
          - code: storage:io_error
            when: Key storage answered with an error. Send the request again.
          - code: storage:conflict
            when: Other nodes kept changing the stored keys. The stored keys are unchanged;
              send the request again.
      security:
      - apiKey: []
      x-required-permission: keys.write
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/keys/{id}/actions/rotate:
    post:
      summary: Rotate the credential of an API key
      description: |-
        Replaces the credential of a key and returns the new one. The key ID, description, grants, `createdAt` and `expiresAt` are all kept, so a rotation is how a credential is replaced without rebuilding the grants and without anything that records the key ID going stale.

        The previous credential stops working on the answering node immediately and across all other nodes within `EXOFIND_AUTH_REFRESH_INTERVAL`, as nodes accept cached keys until their next storage read. That interval is the window in which clients can move to the new credential.

        As when a key is created, the full secret credential is returned only in this response because credentials are stored only as hashes. A lost credential cannot be recovered; rotate the key again. Server logs record the key `id`, never the credential value.

        Served by whichever node receives the request.

        Requires the `keys.write` permission, which is not about one index. The `admin` role includes it.
      operationId: rotateKey
      tags:
      - API keys
      parameters:
      - description: ID of the key whose credential is replaced.
        example: 4ff6b760264c1918
        name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The credential was replaced. The `credential` in this response
            is the only copy returned.
          content:
            application/json:
              examples:
                rotated:
                  value:
                    credential: exok_4ff6b760264c1918_ePQcdT1O9HSATZoXfDbT8hhHGsP9VpZH
                    key:
                      id: 4ff6b760264c1918
                      description: the search backend
                      grants:
                      - permissions:
                        - indexes.read
                        - search
                        indexes:
                        - products
                      createdAt: 2026-08-16T12:09:33.198275Z
                      expiresAt: 2027-01-01T00:00:00Z
              schema:
                $ref: "#/components/schemas/CreatedKey"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `keys.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `keys.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `keys.write` permission.
        "404":
          description: |-
            No key has this ID.

            Error codes: `auth:key:not_found` - No key is stored under this ID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:key:not_found
            when: No key is stored under this ID.
        "409":
          description: |-
            The credential could not be replaced. The stored keys are unchanged and the previous credential still works.

            Error codes: `storage:unavailable` - The node is not configured with key storage. `storage:io_error` - Key storage answered with an error. Send the request again. `storage:conflict` - Other nodes kept changing the stored keys. The stored keys are unchanged; send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:unavailable
            when: The node is not configured with key storage.
          - code: storage:io_error
            when: Key storage answered with an error. Send the request again.
          - code: storage:conflict
            when: Other nodes kept changing the stored keys. The stored keys are unchanged;
              send the request again.
      security:
      - apiKey: []
      x-required-permission: keys.write
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/registry/actions/repair:
    post:
      summary: Repair the registry from storage
      description: |-
        Registers every `synced` generation that storage holds and the registry does not name. The repair operation only adds entries: it keeps existing entries as stored and never deletes an index, a generation, or storage data. If the registry is absent, the repair writes it fresh. If the registry is corrupt, the repair replaces it with one rebuilt from storage.

        When `"promoteNewest": true`, each index created by the repair answers for its highest-numbered generation. Hand-named generations are not selected. Indexes that are already registered keep what they answer for. When omitted or false, created indexes answer for nothing until a generation is promoted.

        Storage of a deleted index or generation, which the audit reports with `removedAt`, is skipped until the sweep removes it. Naming it in `"restore"` takes the removal mark off and registers it like any other unregistered storage, which is how a delete is taken back within the grace period.

        The write is conditional and rebuilds on top of concurrent registry changes. The answering node applies the repaired registry immediately; other nodes pick it up within `EXOFIND_INDEXES_REFRESH_INTERVAL`.

        Served by whichever node receives the request and never forwarded. Answers only in object storage mode.

        Requires the `registry.repair` permission, which is not about one index. The `admin` role includes it.
      operationId: repairRegistry
      tags:
      - Registry
      requestBody:
        content:
          application/json:
            examples:
              repair:
                value:
                  promoteNewest: true
                  restore:
                  - books
            schema:
              $ref: "#/components/schemas/RegistryRepairRequest"
        required: false
      responses:
        "200":
          description: A summary of the changes made by the repair.
          content:
            application/json:
              examples:
                repaired:
                  value:
                    createdIndexes:
                    - products
                    addedGenerations:
                    - products@2
                    promoted:
                    - products@2
                    restored:
                    - products
              schema:
                $ref: "#/components/schemas/RegistryRepairResponse"
        "400":
          description: |-
            An entry of `restore` is not a valid index or generation name.

            Error codes: `index:name_invalid` - An entry of `restore` is not a valid index or generation name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:name_invalid
            when: An entry of `restore` is not a valid index or generation name.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `registry.repair` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `registry.repair` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `registry.repair` permission.
        "409":
          description: |-
            The endpoint was called on a node configured with local storage, or writing the repaired registry failed. The registry remains unchanged.

            Error codes: `index:registry:audit_unavailable` - The node stores indexes on local disk, where there is no shared registry to repair. `storage:conflict` - The registry kept being written by other nodes. The registry is unchanged; send the request again. `storage:io_error` - Registry storage answered with an error. The registry is unchanged; send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:registry:audit_unavailable
            when: "The node stores indexes on local disk, where there is no shared\
              \ registry to repair."
          - code: storage:conflict
            when: The registry kept being written by other nodes. The registry is
              unchanged; send the request again.
          - code: storage:io_error
            when: Registry storage answered with an error. The registry is unchanged;
              send the request again.
      security:
      - apiKey: []
      x-required-permission: registry.repair
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/registry/audit:
    get:
      summary: Audit the registry against storage
      description: |-
        Reads the registry and remote storage, comparing the two without changing either.

        Unregistered generations in storage indicate interrupted rollouts, or deleted indexes and generations whose storage waits for the sweep that removes it; those carry `removedAt`. Registered generations missing from storage have no data available to pull.

        Served by whichever node receives the request and never forwarded. Answers only in object storage mode.

        Requires the `registry.audit` permission, which is not about one index. The `admin` role includes it.
      operationId: auditRegistry
      tags:
      - Registry
      responses:
        "200":
          description: Comparison of the shared registry with remote storage.
          content:
            application/json:
              examples:
                audit:
                  value:
                    registry: present
                    indexes:
                    - name: products
                      registered: true
                      live: "2"
                      generations:
                      - name: "1"
                        registered: true
                        stored: synced
                      - name: "2"
                        registered: true
                        stored: synced
                    - name: staging
                      registered: false
                      removedAt: 2026-09-03T10:15:00Z
                      generations:
                      - name: "1"
                        registered: false
                        stored: synced
                    unusable: []
              schema:
                $ref: "#/components/schemas/RegistryAuditResponse"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `registry.audit` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `registry.audit` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `registry.audit` permission.
        "409":
          description: |-
            The endpoint was called on a node configured with local storage rather than shared storage.

            Error codes: `index:registry:audit_unavailable` - The node stores indexes on local disk, where there is no shared registry to audit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:registry:audit_unavailable
            when: "The node stores indexes on local disk, where there is no shared\
              \ registry to audit."
      security:
      - apiKey: []
      x-required-permission: registry.audit
      x-permission-scope: deployment
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/reindexes:
    get:
      summary: List reindex jobs
      description: |-
        Lists every reindex job across the deployment, including finished jobs, ordered by index name. Served from durable job records, so any node can serve the request and returns the same response.

        Jobs on indexes where the key lacks permissions are omitted rather than refused.

        `index` keeps the job of one index and `phase` the jobs in the named phases, so a poll for running jobs is one request. `prefix` keeps the jobs whose index name starts with it. `limit` caps the answer, and a listing cut short names the last index in `next`; pass it as `after` to read on. See [Listings](https://exofind.dev/reference/admin-api/#listings).

        Requires the `indexes.read` permission on at least one index. The `reader`, `writer` and `admin` roles include it.
      operationId: listReindexes
      tags:
      - Reindexes
      parameters:
      - description: "The index name to continue after, as the `next` field of the\
          \ previous response gave it. The job of the named index is not included."
        example: products
        name: after
        in: query
        schema:
          type: string
      - description: Keeps only the job of this index. Answers an empty listing rather
          than `404` when the index has no job.
        example: products
        name: index
        in: query
        schema:
          type: string
      - description: "Most jobs to answer. Without it the whole listing is answered.\
          \ When more remain, the response carries the last name in `next`."
        schema:
          maximum: 1000
          minimum: 1
          type: integer
        name: limit
        in: query
      - description: "Keeps only the jobs in these phases. Repeat the parameter or\
          \ separate the phases with commas, as `phase=copying,replaying`."
        example: copying
        name: phase
        in: query
        schema:
          type: array
          items:
            type: string
      - description: Keeps only the jobs whose index name starts with this text.
        name: prefix
        in: query
        schema:
          type: string
      responses:
        "200":
          description: "The job records visible to the key, ordered by index name."
          content:
            application/json:
              examples:
                jobs:
                  value:
                    reindexes:
                    - id: 6f1c2a9d8b3e4c05
                      index: products
                      target: products@2
                      source: products@1
                      phase: copying
                      promote: auto
                      documentsCopied: 125000
                      sourceDocuments: 2400000
                      backlog: 4100
                      error: null
                      startedBy: 3f9a1c7e2b8d4650
                      node: node-a-7f21
                      startedAt: 2026-08-28T10:15:30Z
                      updatedAt: 2026-08-28T10:16:02Z
                      finishedAt: null
              schema:
                $ref: "#/components/schemas/ReindexListResponse"
        "400":
          description: |-
            The `limit` parameter is out of range, or `phase` names no phase.

            Error codes: `request:limit_out_of_range` - The `limit` parameter is not a whole number from 1 to 1000. `reindex:phase_invalid` - A `phase` parameter names no reindex phase. The `value` argument carries what was sent.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:limit_out_of_range
            when: The `limit` parameter is not a whole number from 1 to 1000.
          - code: reindex:phase_invalid
            when: A `phase` parameter names no reindex phase. The `value` argument
              carries what was sent.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
        "409":
          description: |-
            The job records could not be read.

            Error codes: `storage:io_error` - The records of the reindexes could not be read. Send the request again once the storage responds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:io_error
            when: The records of the reindexes could not be read. Send the request
              again once the storage responds.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: any-index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/reindexes/{name}:
    get:
      summary: Get reindex job status
      description: |-
        Returns the status of a reindex job on an index, including finished jobs. Served from the durable job record, so any node can serve the request and returns the same response.

        The name is the index the job belongs to, or one generation of that index. If no job exists for the index, the server returns `404` with the error code `reindex:not_found`.

        Requires the `indexes.read` permission on the index the path names. The `reader`, `writer` and `admin` roles include it.
      operationId: getReindex
      tags:
      - Reindexes
      parameters:
      - description: "The index, or one generation of it. The job belongs to the index\
          \ in either case."
        example: products
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The job record.
          content:
            application/json:
              examples:
                job:
                  value:
                    id: 6f1c2a9d8b3e4c05
                    index: products
                    target: products@2
                    source: products@1
                    phase: copying
                    promote: auto
                    documentsCopied: 125000
                    sourceDocuments: 2400000
                    backlog: 4100
                    error: null
                    startedBy: 3f9a1c7e2b8d4650
                    node: node-a-7f21
                    startedAt: 2026-08-28T10:15:30Z
                    updatedAt: 2026-08-28T10:16:02Z
                    finishedAt: null
                    freshness: null
              schema:
                $ref: "#/components/schemas/ReindexInfo"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.read` permission.
        "404":
          description: |-
            No job exists for this index, the index does not exist, or the caller key lacks permissions on the index.

            Error codes: `reindex:not_found` - The index has no reindex job. `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: reindex:not_found
            when: The index has no reindex job.
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            The job record could not be read.

            Error codes: `storage:io_error` - The record of the reindex could not be read. Send the request again once the storage responds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: storage:io_error
            when: The record of the reindex could not be read. Send the request again
              once the storage responds.
      security:
      - apiKey: []
      x-required-permission: indexes.read
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/admin/reindexes/{name}/actions/cancel:
    post:
      summary: Cancel a reindex job
      description: |-
        Stops an in-progress job. Tracking on the source ends and the partially populated target generation is left in place, to be removed with `DELETE /v1alpha1/admin/indexes/{target}`. The job record stays readable and reports the `cancelled` phase. Cancelling a finished job changes nothing.

        Runs on the node holding the index.

        Requires the `indexes.reindex` permission on the index the path names. The `admin` role includes it.
      operationId: cancelReindex
      tags:
      - Reindexes
      parameters:
      - description: "The index, or one generation of it."
        example: products
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: The job record as it stands after cancellation.
          content:
            application/json:
              examples:
                job:
                  value:
                    id: 6f1c2a9d8b3e4c05
                    index: products
                    target: products@2
                    source: products@1
                    phase: copying
                    promote: auto
                    documentsCopied: 125000
                    sourceDocuments: 2400000
                    backlog: 4100
                    error: null
                    startedBy: 3f9a1c7e2b8d4650
                    node: node-a-7f21
                    startedAt: 2026-08-28T10:15:30Z
                    updatedAt: 2026-08-28T10:16:02Z
                    finishedAt: null
                    freshness: null
              schema:
                $ref: "#/components/schemas/ReindexInfo"
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `indexes.reindex` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `indexes.reindex` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `indexes.reindex` permission.
        "404":
          description: |-
            No job exists for this index, the index does not exist, or the caller key lacks permissions on the index.

            Error codes: `reindex:not_found` - The index has no reindex job. `index:not_found` - No index or generation has this name, or the key holds no grant covering it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: reindex:not_found
            when: The index has no reindex job.
          - code: index:not_found
            when: "No index or generation has this name, or the key holds no grant\
              \ covering it."
        "409":
          description: |-
            No node is available to write the index.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:no_live_generation` - The index has no live generation. Promote one and send the request again. `storage:io_error` - The record of the reindex could not be written. Send the request again once the storage responds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
          - code: storage:io_error
            when: The record of the reindex could not be written. Send the request
              again once the storage responds.
        "502":
          description: |-
            The request was forwarded to the index writer and the writer did not respond.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
      security:
      - apiKey: []
      x-required-permission: indexes.reindex
      x-permission-scope: index
      x-permission-roles:
      - admin
      x-permission-anonymous: false
  /v1alpha1/indexes/{name}/documents:
    get:
      tags:
      - Documents
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
        description: "Name of the index to read, optionally naming one generation\
          \ as `books@2`."
        example: books
      - name: after
        in: query
        schema:
          type: string
        description: "Primary key to resume reading after. The specified key is omitted\
          \ from the response. Formatted as text matching the key in the path of a\
          \ delete (for example, numeric keys are written as numbers). If no document\
          \ exists under this key, reading resumes from where the key would be positioned\
          \ in the order. Omit to start at the first document."
      - name: limit
        in: query
        schema:
          type:
          - string
          - integer
          maximum: 10000
          minimum: 1
          default: 100
        description: Maximum number of documents to return.
      - description: A freshness token an earlier response returned. The documents
          are read only once the node holds the state it names.
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      responses:
        "200":
          description: "The documents, in primary key order."
          content:
            application/x-ndjson: {}
            application/json:
              examples:
                batch:
                  value:
                    documents:
                    - id: "1"
                      name:
                        sv: blåbärssylt
                      energy: 234
                    - id: "2"
                      name:
                        sv: hallonsylt
                      energy: 241
                    next: "2"
                    freshness: AQoIcHJvZHVjdHMSATIYBw
              schema:
                $ref: "#/components/schemas/ScanResponse"
        "400":
          description: |-
            The index cannot be scanned, or the `limit` parameter is out of range.

            Error codes: `index:no_primary_key` - The index definition declares no primary key, so documents cannot be scanned in key order. `document:source_not_kept` - The index does not store document copies, so a scan has nothing to return. `request:limit_out_of_range` - The `limit` parameter is not a whole number from 1 to 10000. `search:value_invalid` - The `after` parameter cannot be read as the type of the primary key field. `search:freshness:invalid` - The `X-Exofind-Freshness` header carries a token the engine did not issue. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so documents cannot\
              \ be scanned in key order."
          - code: document:source_not_kept
            when: "The index does not store document copies, so a scan has nothing\
              \ to return."
          - code: request:limit_out_of_range
            when: The `limit` parameter is not a whole number from 1 to 10000.
          - code: search:value_invalid
            when: The `after` parameter cannot be read as the type of the primary
              key field.
          - code: search:freshness:invalid
            when: The `X-Exofind-Freshness` header carries a token the engine did
              not issue. Pass a token back unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.read` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "503":
          description: |-
            The request raced the index being closed to free local resources. Repeating the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      summary: Read documents
      description: |-
        Reads documents back out of an index in primary key order, returning them as originally indexed. Whole-number keys return in numeric order with negative numbers first, and text keys return in UTF-8 byte order.

        Set the `Accept` request header to select the response format. The default format is `application/json`, which also applies to `Accept: */*`. The format `application/x-ndjson` returns one document per line with no outer wrapper, matching byte-for-byte the format accepted by the indexing endpoint. A newline-delimited body contains only documents, so the line count indicates whether more documents are available rather than a `next` key.

        Every response is bounded, so reading an entire index requires a sequence of requests, each passing the previous response's `next` key in the `after` parameter. A single request reads from a point-in-time snapshot of the index and sees committed data only. Across multiple requests, documents indexed under keys that the read has already passed are omitted from subsequent responses.

        Read requests are served directly by whichever node receives them, using data that the node has pulled from storage, and are never forwarded to the writer.

        Requires the `documents.read` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: readDocuments
      x-required-permission: documents.read
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
    post:
      tags:
      - Documents
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
        description: "Name of the index to write to. To write to a specific generation,\
          \ append `@` and the name of the generation, such as `books@2`."
        example: books
      - name: onError
        in: query
        schema:
          type: string
          default: fail
          enum:
          - fail
          - skip
        description: "Behavior when the index refuses a document: `fail` (default)\
          \ stops at the first one and fails the request, while `skip` indexes the\
          \ remaining documents and returns the refused ones under `failed`. A body\
          \ that cannot be read as JSON fails the request either way."
      requestBody:
        content:
          application/x-ndjson:
            schema:
              type: string
              format: binary
          application/json:
            examples:
              documents:
                summary: One document with a locale-specific field
                value:
                  documents:
                  - id: "1"
                    name:
                      sv: blåbärssylt
                      en: blueberry jam
                    tags:
                    - sylt
                    - bär
                    energy: 234
            schema:
              $ref: "#/components/schemas/DocumentsRequest"
        required: true
      responses:
        "200":
          description: The documents were indexed successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentsResponse"
              examples:
                indexed:
                  value:
                    indexed: 2
                    failed: []
                    freshness: AQoIcHJvZHVjdHMSATIYBw
        "400":
          description: |-
            A document was rejected by validation, a line could not be read as JSON, or the request body could not be parsed. The `path` of each error identifies the document and field location as the body carries it, such as `documents[1].nonexistent` for a `documents` array and `[1].nonexistent` for a newline-delimited body. The `arguments` of each error carry the same place as numbers to resume from: `position` for the document, `processed` for how many documents the index took before it, and `line` for the line of a newline-delimited body.

            Error codes: `document:malformed` - A line of the body could not be read as JSON. `request:body_required` - The request carries no documents. `document:not_an_object` - A document is not an object keyed by field name. `document:on_error_invalid` - `onError` is neither `fail` nor `skip`. `document:field_unknown` - A document gives a field the index does not have. `document:field_inside_object` - A document gives a dotted path to a field inside an object instead of the object that holds it. `document:field_required` - A document leaves out a field the definition marks as required. `document:locale_unknown` - A value carries a locale the field does not hold values in. `document:locale_unsupported` - A value carries a locale on a field that is not locale specific. `document:object_required` - A field that holds objects is given a value that is not one. `document:object_unsupported` - A field that does not hold objects is given one. `document:multiple_unsupported` - A field that holds a single value is given several. `document:multiple_per_locale_unsupported` - A field that holds a single value per locale is given several in one locale. `document:object_key_duplicate` - Two values of an object field read the same under the key that tells them apart. `document:number:value_invalid` - A number field is given a value that cannot be read as its type. `document:number:value_out_of_range` - A number field is given a value outside the bounds its definition declares. `document:geo_point:value_invalid` - A geo point field is given a value that is not a latitude and a longitude. `document:geo_point:value_out_of_range` - A geo point field is given a point that is not on the earth. `document:timestamp:value_invalid` - A timestamp field is given a value that is not an ISO 8601 date and time with an offset. `document:vector:value_invalid` - A vector field is given a value that is not an array of floats. `document:vector:value_not_finite` - A vector field is given a value that is not a finite number. `document:vector:dimensions_mismatch` - A vector field is given a vector with other dimensions than the field declares. `document:vector:value_zero` - A vector field compared by cosine is given a vector of only zeros. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again. `request:body_unreadable` - The body stopped arriving part way through. The documents read before that are indexed; send the rest again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: document:malformed
            when: A line of the body could not be read as JSON.
          - code: request:body_required
            when: The request carries no documents.
          - code: document:not_an_object
            when: A document is not an object keyed by field name.
          - code: document:on_error_invalid
            when: '`onError` is neither `fail` nor `skip`.'
          - code: document:field_unknown
            when: A document gives a field the index does not have.
          - code: document:field_inside_object
            when: A document gives a dotted path to a field inside an object instead
              of the object that holds it.
          - code: document:field_required
            when: A document leaves out a field the definition marks as required.
          - code: document:locale_unknown
            when: A value carries a locale the field does not hold values in.
          - code: document:locale_unsupported
            when: A value carries a locale on a field that is not locale specific.
          - code: document:object_required
            when: A field that holds objects is given a value that is not one.
          - code: document:object_unsupported
            when: A field that does not hold objects is given one.
          - code: document:multiple_unsupported
            when: A field that holds a single value is given several.
          - code: document:multiple_per_locale_unsupported
            when: A field that holds a single value per locale is given several in
              one locale.
          - code: document:object_key_duplicate
            when: Two values of an object field read the same under the key that tells
              them apart.
          - code: document:number:value_invalid
            when: A number field is given a value that cannot be read as its type.
          - code: document:number:value_out_of_range
            when: A number field is given a value outside the bounds its definition
              declares.
          - code: document:geo_point:value_invalid
            when: A geo point field is given a value that is not a latitude and a
              longitude.
          - code: document:geo_point:value_out_of_range
            when: A geo point field is given a point that is not on the earth.
          - code: document:timestamp:value_invalid
            when: A timestamp field is given a value that is not an ISO 8601 date
              and time with an offset.
          - code: document:vector:value_invalid
            when: A vector field is given a value that is not an array of floats.
          - code: document:vector:value_not_finite
            when: A vector field is given a value that is not a finite number.
          - code: document:vector:dimensions_mismatch
            when: A vector field is given a vector with other dimensions than the
              field declares.
          - code: document:vector:value_zero
            when: A vector field compared by cosine is given a vector of only zeros.
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
          - code: request:body_unreadable
            when: The body stopped arriving part way through. The documents read before
              that are indexed; send the rest again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.write` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be written to right now.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "413":
          description: |-
            The request body is larger than the node accepts. The node states one size for a body it holds in memory and another for a newline-delimited body it reads as it arrives; the `limit` argument carries the one this request passed, in bytes. A newline-delimited body also carries `processed`, how many documents the index took before the body was cut off, so the rest can be sent again.

            Error codes: `request:body_too_large` - The body passed the size the node accepts for a newline-delimited request. The documents read before that are indexed; `processed` says how many, so the rest can be sent again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:body_too_large
            when: "The body passed the size the node accepts for a newline-delimited\
              \ request. The documents read before that are indexed; `processed` says\
              \ how many, so the rest can be sent again."
        "502":
          description: |-
            The node holding the index did not answer.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The index is not open on the node right now.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      summary: Index documents
      description: |-
        Indexes one or more documents into the specified index. Each document specifies its own primary key. Indexing a document with an existing key replaces the document under that key. Documents in a batch are processed in the order sent. The first refused document halts processing and fails the request; documents processed before the failure remain in the index. Every error names the document it is about: `position` counts the documents of the request from zero, `processed` says how many the index took before the failure, and a newline-delimited body also carries `line`. Send `?onError=skip` to index the rest of the batch instead and read the refused documents from `failed`.

        Format the request body as `application/json` with a `documents` array, or `application/x-ndjson` with one document object per line and no outer wrapper. Newline-delimited documents are indexed as they are read, so the node holds a buffer rather than the whole body and a single request can carry a whole dataset. A JSON body is held in memory and is bounded by a smaller size; both sizes are set by the deployment, and a body past either is refused with `413`.

        Changes become searchable and replicate to remote storage after the index commits. The writer commits automatically based on indexed document volume or elapsed time. To commit changes immediately, call `POST /v1alpha1/admin/indexes/{name}/actions/commit`.

        The operation runs on the index writer node. A write request received by another node is forwarded automatically.

        Requires the `documents.write` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: indexDocuments
      x-required-permission: documents.write
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/indexes/{name}/documents/actions/delete:
    post:
      summary: "Delete documents by keys, query, or all"
      description: |-
        Deletes multiple documents matching a list of primary keys or a search query, or empties the index. The request body must name exactly one of `keys`, `query`, and `all`.

        When deleting by `keys`, all keys are validated before any documents are removed. If any key is invalid, no documents are removed. When deleting by `query`, the operation removes matching committed searchable documents along with any uncommitted documents indexed since the last commit. A `query` requires at least one clause. To empty the index, set `all` to `true`.

        Requires the `documents.delete` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: deleteDocuments
      tags:
      - Documents
      parameters:
      - description: "Name of the index to write to, optionally specifying a generation\
          \ such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            examples:
              keys:
                summary: By primary key
                value:
                  keys:
                  - "1"
                  - "2"
              query:
                summary: By query
                value:
                  query:
                  - field: category
                    match:
                      value: sylt
              all:
                summary: Every document
                value:
                  all: true
            schema:
              $ref: "#/components/schemas/DeleteRequest"
        required: true
      responses:
        "200":
          description: The documents were removed.
          content:
            application/json:
              examples:
                deleted:
                  value:
                    deleted: 3
                    freshness: AQoIcHJvZHVjdHMSATIYBw
              schema:
                $ref: "#/components/schemas/DeleteResponse"
        "400":
          description: |-
            The body does not name what to delete, or it names a key or a query the index cannot use.

            Error codes: `document:delete:target_required` - The body holds none of `keys`, `query`, and `all`. `document:delete:target_conflicting` - The body holds more than one of `keys`, `query`, and `all`. Send one of them. `document:delete:query_empty` - The body holds a `query` without clauses. Send `all` to empty the index. `document:delete:locale_without_query` - The body states a `locale` without a `query`. `search:value_invalid` - A key cannot be read as the type of the primary key field. `document:key_required` - An entry of `keys` carries no key. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named by `keys`. `request:value_required` - A part of `query` that needs a value carries none. The `path` names it. `search:clause:field_required` - A `field` clause of `query` does not name the field to match. `search:clause:match_required` - A `field` clause of `query` does not say what to look for in the field. `search:clause:text_required` - A `text` clause of `query` carries no text to search for. `search:clause:path_required` - A `nested` clause of `query` does not name the object field to match inside. `search:clause:vector_required` - A `knn` clause of `query` carries no vector to find the neighbours of. `search:clause:k_out_of_range` - The `k` of a `knn` clause is missing or not above zero. `search:clause:weight_out_of_range` - The `weight` of a `boost` clause is missing, below zero or not a finite number. `search:clause:slop_out_of_range` - The `slop` of a `text` clause is below zero. `search:clause:slop_unsupported` - A `text` clause sets `slop` without matching as a phrase. `search:clause:join_unsupported` - A `text` clause sets `join` without matching what somebody typed. `search:clause:rankings_too_few` - A `fuse` clause holds fewer than two rankings to fuse. `search:clause:ranking_empty` - A ranking of a `fuse` clause holds nothing to rank by. `search:clause:rank_constant_out_of_range` - The `rankConstant` of a `fuse` clause is not a number above zero. `search:clause:depth_out_of_range` - The `depth` of a `fuse` clause is below one result. `search:clause:interpret_fields_required` - The `interpret` of a `text` clause names no target field. `search:clause:interpret_when_unsupported` - The `when` of an `interpret` target holds a `nested`, `knn` or `fuse` clause. `search:matcher:value_required` - A matcher carries no value to look for. `search:matcher:range_empty` - A range matcher carries no bound. `search:matcher:range_conflicting` - A range matcher combines `gte` with `gt`, or `lte` with `lt`. `search:matcher:origin_required` - A distance matcher carries no `lat` and `lon` to measure from. `search:matcher:radius_required` - A distance matcher does not say how far from the origin values may be. `search:field_unknown` - A clause, sort or facet names a field the index does not have. `search:usage_unsupported` - A clause, sort or facet uses a field in a way the definition does not enable for it. `search:matcher:type_unsupported` - A matcher is used on a field whose type cannot answer it. `search:locale_unsupported` - The `locale` names one the engine has no rules for. `search:no_searchable_fields` - A text clause names no fields and the index has none defined for matching. `search:nested:path_not_nested` - A `nested` clause names a path whose values are flattened. `search:nested:field_not_inside` - A clause inside a `nested` clause names a field outside its path. `search:nested:field_outside` - A clause outside a `nested` clause names a field inside a nested list. `search:nested:clause_unsupported` - A `nested` clause holds a clause that cannot run against a single value, such as `fuse`. `search:interpret:unit_required` - An `interpret` target names a field that is not a number field or declares no `unit`. `search:interpret:fallback_unit_mismatch` - A `fallback` target declares another unit than the target it stands in for. `search:clause:k_required` - A `knn` clause carries no `k`. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: document:delete:target_required
            when: "The body holds none of `keys`, `query`, and `all`."
          - code: document:delete:target_conflicting
            when: "The body holds more than one of `keys`, `query`, and `all`. Send\
              \ one of them."
          - code: document:delete:query_empty
            when: The body holds a `query` without clauses. Send `all` to empty the
              index.
          - code: document:delete:locale_without_query
            when: The body states a `locale` without a `query`.
          - code: search:value_invalid
            when: A key cannot be read as the type of the primary key field.
          - code: document:key_required
            when: An entry of `keys` carries no key.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named by `keys`."
          - code: request:value_required
            when: A part of `query` that needs a value carries none. The `path` names
              it.
          - code: search:clause:field_required
            when: A `field` clause of `query` does not name the field to match.
          - code: search:clause:match_required
            when: A `field` clause of `query` does not say what to look for in the
              field.
          - code: search:clause:text_required
            when: A `text` clause of `query` carries no text to search for.
          - code: search:clause:path_required
            when: A `nested` clause of `query` does not name the object field to match
              inside.
          - code: search:clause:vector_required
            when: A `knn` clause of `query` carries no vector to find the neighbours
              of.
          - code: search:clause:k_out_of_range
            when: The `k` of a `knn` clause is missing or not above zero.
          - code: search:clause:weight_out_of_range
            when: "The `weight` of a `boost` clause is missing, below zero or not\
              \ a finite number."
          - code: search:clause:slop_out_of_range
            when: The `slop` of a `text` clause is below zero.
          - code: search:clause:slop_unsupported
            when: A `text` clause sets `slop` without matching as a phrase.
          - code: search:clause:join_unsupported
            when: A `text` clause sets `join` without matching what somebody typed.
          - code: search:clause:rankings_too_few
            when: A `fuse` clause holds fewer than two rankings to fuse.
          - code: search:clause:ranking_empty
            when: A ranking of a `fuse` clause holds nothing to rank by.
          - code: search:clause:rank_constant_out_of_range
            when: The `rankConstant` of a `fuse` clause is not a number above zero.
          - code: search:clause:depth_out_of_range
            when: The `depth` of a `fuse` clause is below one result.
          - code: search:clause:interpret_fields_required
            when: The `interpret` of a `text` clause names no target field.
          - code: search:clause:interpret_when_unsupported
            when: "The `when` of an `interpret` target holds a `nested`, `knn` or\
              \ `fuse` clause."
          - code: search:matcher:value_required
            when: A matcher carries no value to look for.
          - code: search:matcher:range_empty
            when: A range matcher carries no bound.
          - code: search:matcher:range_conflicting
            when: "A range matcher combines `gte` with `gt`, or `lte` with `lt`."
          - code: search:matcher:origin_required
            when: A distance matcher carries no `lat` and `lon` to measure from.
          - code: search:matcher:radius_required
            when: A distance matcher does not say how far from the origin values may
              be.
          - code: search:field_unknown
            when: "A clause, sort or facet names a field the index does not have."
          - code: search:usage_unsupported
            when: "A clause, sort or facet uses a field in a way the definition does\
              \ not enable for it."
          - code: search:matcher:type_unsupported
            when: A matcher is used on a field whose type cannot answer it.
          - code: search:locale_unsupported
            when: The `locale` names one the engine has no rules for.
          - code: search:no_searchable_fields
            when: A text clause names no fields and the index has none defined for
              matching.
          - code: search:nested:path_not_nested
            when: A `nested` clause names a path whose values are flattened.
          - code: search:nested:field_not_inside
            when: A clause inside a `nested` clause names a field outside its path.
          - code: search:nested:field_outside
            when: A clause outside a `nested` clause names a field inside a nested
              list.
          - code: search:nested:clause_unsupported
            when: "A `nested` clause holds a clause that cannot run against a single\
              \ value, such as `fuse`."
          - code: search:interpret:unit_required
            when: An `interpret` target names a field that is not a number field or
              declares no `unit`.
          - code: search:interpret:fallback_unit_mismatch
            when: A `fallback` target declares another unit than the target it stands
              in for.
          - code: search:clause:k_required
            when: A `knn` clause carries no `k`.
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.delete` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.delete` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.delete` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            No node is available to write the index, the index is currently synchronizing, or the target generation is locked by an active reindex job.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "502":
          description: |-
            The request was forwarded to the index writer and the writer did not respond.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The request raced the index being closed to free local resources. Repeating the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      x-required-permission: documents.delete
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/indexes/{name}/documents/actions/update:
    post:
      tags:
      - Documents
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
        description: "Name of the index to write to, optionally specifying a generation\
          \ such as `books@2`."
        example: books
      - name: missing
        in: query
        schema:
          type: string
          default: fail
          enum:
          - fail
          - skip
        description: "Behavior when a document key does not exist: `fail` (default)\
          \ fails the request, while `skip` updates the remaining documents and returns\
          \ the missing keys under `missing`."
      - name: onError
        in: query
        schema:
          type: string
          default: fail
          enum:
          - fail
          - skip
        description: "Behavior when the index refuses a change: `fail` (default) stops\
          \ at the first one and fails the request, while `skip` applies the remaining\
          \ changes and returns the refused ones under `failed`. A key nothing is\
          \ indexed under is governed by `missing` instead when that says `skip`."
      requestBody:
        content:
          application/x-ndjson:
            schema:
              type: string
              format: binary
          application/json:
            examples:
              changes:
                summary: Two documents changed by path
                value:
                  documents:
                  - id: "1"
                    price: 34.5
                    inStock: true
                  - id: "2"
                    price: 12.0
                    variants[sku=V-2].price: 29.0
            schema:
              $ref: "#/components/schemas/UpdateRequest"
        required: true
      responses:
        "200":
          description: The documents were updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UpdateResponse"
              examples:
                updated:
                  value:
                    updated: 1998
                    missing: []
                    failed: []
                    freshness: AQoIcHJvZHVjdHMSATIYBw
        "400":
          description: |-
            A change failed validation, or a path in it names something the index or the document does not hold. The `arguments` of each error carry where in the batch the change sat and how much of the batch had landed: `position`, `processed`, and `line` for a newline-delimited body.

            Error codes: `document:not_found` - A document the change names is not indexed and `missing` is `fail`. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named. `document:source_not_kept` - The index does not store document copies. Send the complete document instead. `document:patch:path_invalid` - A path in the change could not be read. `document:patch:field_unknown` - A path reaches into a field the index does not have. `document:patch:selector_unsupported` - A path names one value of a field that holds neither locale variants nor objects. `document:locale_unknown` - A path names a locale the field holds no variant for. `document:patch:add_unsupported` - A change adds a value to a field that holds a single value. `document:patch:not_an_object` - A path reaches inside a field whose values are not objects. `document:patch:selector_required` - A path reaches into a list of objects without saying which value. `document:patch:no_match` - A selector names no value the document holds. A selector never creates the value it names. `document:patch:key_unsupported` - A path names one value of a list by a key that the field declares none of. Match on a field inside the value instead. `document:patch:match_not_an_object` - A path matches on a field inside a list whose values are not objects. `document:patch:add_reaches_inside` - A path reaches inside a value that the same change adds, which does not exist yet. Give the whole value instead. `document:patch:missing_invalid` - `missing` is neither `fail` nor `skip`. `document:on_error_invalid` - `onError` is neither `fail` nor `skip`. `request:body_required` - The request carries no changes. `document:malformed` - A line of the body could not be read as JSON. `document:not_an_object` - A change is not an object keyed by path. `document:field_unknown` - A document gives a field the index does not have. `document:field_inside_object` - A document gives a dotted path to a field inside an object instead of the object that holds it. `document:field_required` - A document leaves out a field the definition marks as required. `document:locale_unsupported` - A value carries a locale on a field that is not locale specific. `document:object_required` - A field that holds objects is given a value that is not one. `document:object_unsupported` - A field that does not hold objects is given one. `document:multiple_unsupported` - A field that holds a single value is given several. `document:multiple_per_locale_unsupported` - A field that holds a single value per locale is given several in one locale. `document:object_key_duplicate` - Two values of an object field read the same under the key that tells them apart. `document:number:value_invalid` - A number field is given a value that cannot be read as its type. `document:number:value_out_of_range` - A number field is given a value outside the bounds its definition declares. `document:geo_point:value_invalid` - A geo point field is given a value that is not a latitude and a longitude. `document:geo_point:value_out_of_range` - A geo point field is given a point that is not on the earth. `document:timestamp:value_invalid` - A timestamp field is given a value that is not an ISO 8601 date and time with an offset. `document:vector:value_invalid` - A vector field is given a value that is not an array of floats. `document:vector:value_not_finite` - A vector field is given a value that is not a finite number. `document:vector:dimensions_mismatch` - A vector field is given a vector with other dimensions than the field declares. `document:vector:value_zero` - A vector field compared by cosine is given a vector of only zeros. `document:primary_key_required` - A change to some of a document carries no primary key, so it names no document. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again. `request:body_unreadable` - The body stopped arriving part way through. The changes read before that are applied; send the rest again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: document:not_found
            when: A document the change names is not indexed and `missing` is `fail`.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named."
          - code: document:source_not_kept
            when: The index does not store document copies. Send the complete document
              instead.
          - code: document:patch:path_invalid
            when: A path in the change could not be read.
          - code: document:patch:field_unknown
            when: A path reaches into a field the index does not have.
          - code: document:patch:selector_unsupported
            when: A path names one value of a field that holds neither locale variants
              nor objects.
          - code: document:locale_unknown
            when: A path names a locale the field holds no variant for.
          - code: document:patch:add_unsupported
            when: A change adds a value to a field that holds a single value.
          - code: document:patch:not_an_object
            when: A path reaches inside a field whose values are not objects.
          - code: document:patch:selector_required
            when: A path reaches into a list of objects without saying which value.
          - code: document:patch:no_match
            when: A selector names no value the document holds. A selector never creates
              the value it names.
          - code: document:patch:key_unsupported
            when: A path names one value of a list by a key that the field declares
              none of. Match on a field inside the value instead.
          - code: document:patch:match_not_an_object
            when: A path matches on a field inside a list whose values are not objects.
          - code: document:patch:add_reaches_inside
            when: "A path reaches inside a value that the same change adds, which\
              \ does not exist yet. Give the whole value instead."
          - code: document:patch:missing_invalid
            when: '`missing` is neither `fail` nor `skip`.'
          - code: document:on_error_invalid
            when: '`onError` is neither `fail` nor `skip`.'
          - code: request:body_required
            when: The request carries no changes.
          - code: document:malformed
            when: A line of the body could not be read as JSON.
          - code: document:not_an_object
            when: A change is not an object keyed by path.
          - code: document:field_unknown
            when: A document gives a field the index does not have.
          - code: document:field_inside_object
            when: A document gives a dotted path to a field inside an object instead
              of the object that holds it.
          - code: document:field_required
            when: A document leaves out a field the definition marks as required.
          - code: document:locale_unsupported
            when: A value carries a locale on a field that is not locale specific.
          - code: document:object_required
            when: A field that holds objects is given a value that is not one.
          - code: document:object_unsupported
            when: A field that does not hold objects is given one.
          - code: document:multiple_unsupported
            when: A field that holds a single value is given several.
          - code: document:multiple_per_locale_unsupported
            when: A field that holds a single value per locale is given several in
              one locale.
          - code: document:object_key_duplicate
            when: Two values of an object field read the same under the key that tells
              them apart.
          - code: document:number:value_invalid
            when: A number field is given a value that cannot be read as its type.
          - code: document:number:value_out_of_range
            when: A number field is given a value outside the bounds its definition
              declares.
          - code: document:geo_point:value_invalid
            when: A geo point field is given a value that is not a latitude and a
              longitude.
          - code: document:geo_point:value_out_of_range
            when: A geo point field is given a point that is not on the earth.
          - code: document:timestamp:value_invalid
            when: A timestamp field is given a value that is not an ISO 8601 date
              and time with an offset.
          - code: document:vector:value_invalid
            when: A vector field is given a value that is not an array of floats.
          - code: document:vector:value_not_finite
            when: A vector field is given a value that is not a finite number.
          - code: document:vector:dimensions_mismatch
            when: A vector field is given a vector with other dimensions than the
              field declares.
          - code: document:vector:value_zero
            when: A vector field compared by cosine is given a vector of only zeros.
          - code: document:primary_key_required
            when: "A change to some of a document carries no primary key, so it names\
              \ no document."
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
          - code: request:body_unreadable
            when: The body stopped arriving part way through. The changes read before
              that are applied; send the rest again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.write` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be written to right now.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "413":
          description: |-
            The request body is larger than the node accepts. The node states one size for a body it holds in memory and another for a newline-delimited body it reads as it arrives; the `limit` argument carries the one this request passed, in bytes. A newline-delimited body also carries `processed`, how many documents the index took before the body was cut off, so the rest can be sent again.

            Error codes: `request:body_too_large` - The body passed the size the node accepts for a newline-delimited request. The changes read before that are applied; `processed` says how many, so the rest can be sent again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:body_too_large
            when: "The body passed the size the node accepts for a newline-delimited\
              \ request. The changes read before that are applied; `processed` says\
              \ how many, so the rest can be sent again."
        "502":
          description: |-
            The node holding the index did not answer.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The index is not open on the node right now.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      summary: Update fields of existing documents
      description: |-
        Changes named parts of documents already in the index, leaving the rest of each document unchanged. Each key in a change object is a path naming a location in the document: a path with a value replaces what the path names, a path set to `null` empties what it names, and an omitted path leaves the existing value unchanged.

        The path replaces exactly what it names and leaves surrounding content unchanged. `variants` replaces every value of the field, `variants[sku=V-2]` replaces the object value whose `sku` field reads as `V-2`, and `variants[sku=V-2].price` replaces one field inside that value. Similarly, `title` replaces every variant and `title[sv]` replaces the Swedish variant. `variants[]` adds a value to the values the field holds.

        Send `application/json` with a `documents` array containing change objects, or `application/x-ndjson` with one change object per line and no outer wrapper.

        Unlike indexing, this endpoint describes modifications rather than desired state and requires existing documents. Multiple updates to the same document in a single batch apply in the order provided, and the updated document is validated as a whole.

        Changes in a batch are applied in the order sent. The first refused change halts processing and fails the request; changes applied before the failure remain in the index. Every error names the change it is about: `position` counts the changes of the request from zero, `processed` says how many the index applied before the failure, and a newline-delimited body also carries `line`. Send `?onError=skip` to apply the rest of the batch instead and read the refused changes from `failed`.

        The index has to declare a primary key and retain document source copies.

        Requires the `documents.write` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: updateDocuments
      x-required-permission: documents.write
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/indexes/{name}/documents/{key}:
    get:
      summary: Read a document by key
      description: |-
        Reads the document indexed under the specified primary key, returning it as originally indexed. The document sits under `document`, so send that value back to `POST /v1alpha1/indexes/{name}/documents` to index it again.

        The read is answered from a point-in-time snapshot of the index and sees committed data only, so a document indexed since the last commit is reported as missing and one removed since the last commit is still returned. To read a write back as soon as it lands, pass the freshness token the write returned in the `X-Exofind-Freshness` header.

        The index has to declare a primary key and retain document source copies.

        Read requests are served directly by whichever node receives them, using data that the node has pulled from storage, and are never forwarded to the writer.

        Requires the `documents.read` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: readDocument
      tags:
      - Documents
      parameters:
      - description: Primary key of the document to read. Parsed according to the
          key field type.
        example: "1"
        name: key
        in: path
        required: true
        schema:
          type: string
      - description: "Name of the index to read, optionally naming one generation\
          \ as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: A freshness token an earlier response returned. The document
          is read only once the node holds the state it names.
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      responses:
        "200":
          description: "The document, formatted as originally indexed."
          content:
            application/json:
              examples:
                document:
                  value:
                    document:
                      id: "1"
                      name:
                        sv: blåbärssylt
                      energy: 234
                    freshness: AQoIcHJvZHVjdHMSATIYBw
              schema:
                $ref: "#/components/schemas/DocumentResponse"
        "400":
          description: |-
            The key cannot be read as the type of the primary key field, the index declares no primary key, or the index keeps no copies of its documents.

            Error codes: `search:value_invalid` - The key in the path cannot be read as the type of the primary key field. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named. `document:source_not_kept` - The index does not store document copies, so there is nothing to return. `search:freshness:invalid` - The `X-Exofind-Freshness` header carries a token the engine did not issue. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:value_invalid
            when: The key in the path cannot be read as the type of the primary key
              field.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named."
          - code: document:source_not_kept
            when: "The index does not store document copies, so there is nothing to\
              \ return."
          - code: search:freshness:invalid
            when: The `X-Exofind-Freshness` header carries a token the engine did
              not issue. Pass a token back unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.read` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.read` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.read` permission.
        "404":
          description: |-
            Nothing is indexed under the specified key, no index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `document:not_found` - Nothing is indexed under the key in the path, as of the last commit. `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: document:not_found
            when: "Nothing is indexed under the key in the path, as of the last commit."
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "503":
          description: |-
            The request raced the index being closed to free local resources. Repeating the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      x-required-permission: documents.read
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
    put:
      summary: Index a document under a key
      description: |-
        Indexes the document in the request body under the primary key in the path, replacing whatever is indexed under that key. Indexing is a statement of desired state, so repeating the request produces the same outcome and the response says the same thing whether or not a document was indexed under the key before the request.

        The body is one document object, formatted like an entry of `POST /v1alpha1/indexes/{name}/documents`. Leave the primary key field out. The document is indexed under the key in the path. A body that does give the primary key field has to give that same key.

        A document sent this way goes to the index as a whole. Use `PATCH` on the same path to change named parts of a document and leave the rest.

        One request carries one document. To load a dataset, send batches to `POST /v1alpha1/indexes/{name}/documents`, which takes a newline delimited body and costs one request for each batch instead of one for each document.

        Changes become searchable and replicate to remote storage after the index commits. The writer commits automatically based on indexed document volume or elapsed time. To commit changes immediately, call `POST /v1alpha1/admin/indexes/{name}/actions/commit`.

        The index definition has to declare a primary key.

        The operation runs on the index writer node. A write request received by another node is forwarded automatically.

        Requires the `documents.write` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: putDocument
      tags:
      - Documents
      parameters:
      - description: Primary key to index the document under. Parsed according to
          the key field type.
        example: "1"
        name: key
        in: path
        required: true
        schema:
          type: string
      - description: "Name of the index to write to, optionally specifying a generation\
          \ such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        description: "The document, keyed by field name. The primary key field can\
          \ be left out, because the document is indexed under the key in the path."
        content:
          application/json:
            examples:
              document:
                summary: A document with a locale-specific field
                value:
                  name:
                    sv: blåbärssylt
                  tags:
                  - sylt
                  - bär
                  energy: 234
            schema:
              type: object
        required: true
      responses:
        "204":
          description: "The document was indexed, whether or not a document existed\
            \ under the specified key."
        "400":
          description: |-
            The key cannot be read as the type of the primary key field, the index declares no primary key, the body names another document than the path, or the index refused the document.

            Error codes: `search:value_invalid` - The key in the path cannot be read as the type of the primary key field. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named. `document:key_conflicting` - The body gives the primary key field a value other than the key in the path. `request:body_required` - The request carries no document. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:value_invalid
            when: The key in the path cannot be read as the type of the primary key
              field.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named."
          - code: document:key_conflicting
            when: The body gives the primary key field a value other than the key
              in the path.
          - code: request:body_required
            when: The request carries no document.
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.write` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            No node is available to write the index, the index is currently synchronizing, or the target generation is locked by an active reindex job.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "502":
          description: |-
            The request was forwarded to the index writer and the writer did not respond.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The request raced the index being closed to free local resources. Repeating the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      x-required-permission: documents.write
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
    patch:
      summary: Update fields of one document
      description: |-
        Changes named parts of a single document, leaving the remaining fields unchanged. The request body is a single change object formatted like an entry in `POST /documents/actions/update`, with the primary key supplied in the URL path: each key is a path naming a location in the document, a path with a value replaces what the path names, a path set to `null` empties what it names, and an omitted path leaves the existing value unchanged.

        The body may repeat the primary key field as long as it matches the key specified in the path.

        Unlike indexing, this endpoint describes modifications rather than desired state; requesting an update for an unindexed key returns `404` rather than creating a document. The updated document is validated as a whole.

        The index has to declare a primary key and retain document source copies.

        Requires the `documents.write` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: updateDocument
      tags:
      - Documents
      parameters:
      - description: Primary key of the document to change. Parsed according to the
          key field type.
        example: "1"
        name: key
        in: path
        required: true
        schema:
          type: string
      - description: "Name of the index to write to, optionally specifying a generation\
          \ such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        description: "The places to change, keyed by path. A path with a value replaces\
          \ what the path names, a path set to `null` empties what the path names,\
          \ and an omitted path leaves what it would name unchanged."
        content:
          application/json:
            examples:
              change:
                value:
                  price: 34.5
                  variants[sku=V-2].price: 29.0
            schema:
              type: object
        required: true
      responses:
        "204":
          description: The document was changed.
        "400":
          description: |-
            The change was rejected by validation, or a path in it names something the index or the document does not hold. A path is refused for the same reasons as in a batch update.

            Error codes: `search:value_invalid` - The key in the path cannot be read as the type of the primary key field. `document:key_conflicting` - The body gives the primary key field a value other than the key in the path. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named. `document:source_not_kept` - The index does not store document copies. Send the complete document instead. `document:patch:path_invalid` - A path in the change could not be read. `document:patch:no_match` - A selector names no value the document holds. A selector never creates the value it names. `document:patch:field_unknown` - A path reaches into a field the index does not have. `document:patch:not_an_object` - A path reaches inside a field whose values are not objects. `document:patch:selector_required` - A path reaches into a list of objects without saying which value. `document:patch:selector_unsupported` - A path names one value of a field that holds neither locale variants nor objects. `document:locale_unknown` - A path names a locale the field holds no variant for. `document:patch:add_unsupported` - The change adds a value to a field that holds a single value. Name the field on its own to replace it. `document:patch:add_reaches_inside` - A path reaches inside a value that the same change adds, which does not exist yet. Give the whole value instead. `document:patch:key_unsupported` - A path names one value of a list by a key that the field declares none of. Match on a field inside the value instead. `document:patch:match_not_an_object` - A path matches on a field inside a list whose values are not objects. `request:body_required` - The request carries no change. `document:field_unknown` - A document gives a field the index does not have. `document:field_inside_object` - A document gives a dotted path to a field inside an object instead of the object that holds it. `document:field_required` - A document leaves out a field the definition marks as required. `document:locale_unsupported` - A value carries a locale on a field that is not locale specific. `document:object_required` - A field that holds objects is given a value that is not one. `document:object_unsupported` - A field that does not hold objects is given one. `document:multiple_unsupported` - A field that holds a single value is given several. `document:multiple_per_locale_unsupported` - A field that holds a single value per locale is given several in one locale. `document:object_key_duplicate` - Two values of an object field read the same under the key that tells them apart. `document:number:value_invalid` - A number field is given a value that cannot be read as its type. `document:number:value_out_of_range` - A number field is given a value outside the bounds its definition declares. `document:geo_point:value_invalid` - A geo point field is given a value that is not a latitude and a longitude. `document:geo_point:value_out_of_range` - A geo point field is given a point that is not on the earth. `document:timestamp:value_invalid` - A timestamp field is given a value that is not an ISO 8601 date and time with an offset. `document:vector:value_invalid` - A vector field is given a value that is not an array of floats. `document:vector:value_not_finite` - A vector field is given a value that is not a finite number. `document:vector:dimensions_mismatch` - A vector field is given a vector with other dimensions than the field declares. `document:vector:value_zero` - A vector field compared by cosine is given a vector of only zeros. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:value_invalid
            when: The key in the path cannot be read as the type of the primary key
              field.
          - code: document:key_conflicting
            when: The body gives the primary key field a value other than the key
              in the path.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named."
          - code: document:source_not_kept
            when: The index does not store document copies. Send the complete document
              instead.
          - code: document:patch:path_invalid
            when: A path in the change could not be read.
          - code: document:patch:no_match
            when: A selector names no value the document holds. A selector never creates
              the value it names.
          - code: document:patch:field_unknown
            when: A path reaches into a field the index does not have.
          - code: document:patch:not_an_object
            when: A path reaches inside a field whose values are not objects.
          - code: document:patch:selector_required
            when: A path reaches into a list of objects without saying which value.
          - code: document:patch:selector_unsupported
            when: A path names one value of a field that holds neither locale variants
              nor objects.
          - code: document:locale_unknown
            when: A path names a locale the field holds no variant for.
          - code: document:patch:add_unsupported
            when: The change adds a value to a field that holds a single value. Name
              the field on its own to replace it.
          - code: document:patch:add_reaches_inside
            when: "A path reaches inside a value that the same change adds, which\
              \ does not exist yet. Give the whole value instead."
          - code: document:patch:key_unsupported
            when: A path names one value of a list by a key that the field declares
              none of. Match on a field inside the value instead.
          - code: document:patch:match_not_an_object
            when: A path matches on a field inside a list whose values are not objects.
          - code: request:body_required
            when: The request carries no change.
          - code: document:field_unknown
            when: A document gives a field the index does not have.
          - code: document:field_inside_object
            when: A document gives a dotted path to a field inside an object instead
              of the object that holds it.
          - code: document:field_required
            when: A document leaves out a field the definition marks as required.
          - code: document:locale_unsupported
            when: A value carries a locale on a field that is not locale specific.
          - code: document:object_required
            when: A field that holds objects is given a value that is not one.
          - code: document:object_unsupported
            when: A field that does not hold objects is given one.
          - code: document:multiple_unsupported
            when: A field that holds a single value is given several.
          - code: document:multiple_per_locale_unsupported
            when: A field that holds a single value per locale is given several in
              one locale.
          - code: document:object_key_duplicate
            when: Two values of an object field read the same under the key that tells
              them apart.
          - code: document:number:value_invalid
            when: A number field is given a value that cannot be read as its type.
          - code: document:number:value_out_of_range
            when: A number field is given a value outside the bounds its definition
              declares.
          - code: document:geo_point:value_invalid
            when: A geo point field is given a value that is not a latitude and a
              longitude.
          - code: document:geo_point:value_out_of_range
            when: A geo point field is given a point that is not on the earth.
          - code: document:timestamp:value_invalid
            when: A timestamp field is given a value that is not an ISO 8601 date
              and time with an offset.
          - code: document:vector:value_invalid
            when: A vector field is given a value that is not an array of floats.
          - code: document:vector:value_not_finite
            when: A vector field is given a value that is not a finite number.
          - code: document:vector:dimensions_mismatch
            when: A vector field is given a vector with other dimensions than the
              field declares.
          - code: document:vector:value_zero
            when: A vector field compared by cosine is given a vector of only zeros.
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.write` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.write` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.write` permission.
        "404":
          description: |-
            Nothing is indexed under the key, no index with the specified name exists on this node, or the caller key lacks permissions on the index.

            Error codes: `document:not_found` - Nothing is indexed under the key. Index the document whole first. `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: document:not_found
            when: Nothing is indexed under the key. Index the document whole first.
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be written to right now.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "502":
          description: |-
            The node holding the index did not answer.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The index is not open on the node right now.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      x-required-permission: documents.write
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
    delete:
      summary: Delete a document by key
      description: |-
        Removes the document indexed under the specified primary key. Removing a document is a statement of desired state, so requesting the deletion of an unindexed key is not an error and returns status `204`.

        The index definition has to declare a primary key.

        Requires the `documents.delete` permission on the index the path names. The `writer` and `admin` roles include it.
      operationId: deleteDocument
      tags:
      - Documents
      parameters:
      - description: Primary key of the document to remove. Parsed according to the
          key field type.
        example: "1"
        name: key
        in: path
        required: true
        schema:
          type: string
      - description: "Name of the index to write to, optionally specifying a generation\
          \ such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "204":
          description: "The document was removed, whether or not a document existed\
            \ under the specified key."
        "400":
          description: |-
            The key cannot be read as the type of the primary key field, or the index declares no primary key.

            Error codes: `search:value_invalid` - The key in the path cannot be read as the type of the primary key field. `index:no_primary_key` - The index definition declares no primary key, so a document cannot be named. `index:generation:unsettled` - The generation the index serves from kept changing while the write was made. Send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:value_invalid
            when: The key in the path cannot be read as the type of the primary key
              field.
          - code: index:no_primary_key
            when: "The index definition declares no primary key, so a document cannot\
              \ be named."
          - code: index:generation:unsettled
            when: The generation the index serves from kept changing while the write
              was made. Send the request again.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `documents.delete` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `documents.delete` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `documents.delete` permission.
        "404":
          description: |-
            No index with the specified name exists on this node, or the API key lacks permissions on the index.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            No node is available to write the index, the index is currently synchronizing, or the target generation is locked by an active reindex job.

            Error codes: `indexer:unavailable` - No node is available to write the index. Send the request again once one is. `index:out_of_date` - The index is synchronizing. Send the request again. `index:readonly` - The node lost the writer role while the request ran. Send the request again to reach the new writer. `reindex:target_busy` - An active reindex job holds the target generation. Wait for the job, or write to another generation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unavailable
            when: No node is available to write the index. Send the request again
              once one is.
          - code: index:out_of_date
            when: The index is synchronizing. Send the request again.
          - code: index:readonly
            when: The node lost the writer role while the request ran. Send the request
              again to reach the new writer.
          - code: reindex:target_busy
            when: "An active reindex job holds the target generation. Wait for the\
              \ job, or write to another generation."
        "502":
          description: |-
            The request was forwarded to the index writer and the writer did not respond.

            Error codes: `indexer:unreachable` - The request was forwarded to the index writer and the writer did not answer. Send it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: indexer:unreachable
            when: The request was forwarded to the index writer and the writer did
              not answer. Send it again.
        "503":
          description: |-
            The request raced the index being closed to free local resources. Repeating the request reopens the index.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
      security:
      - apiKey: []
      x-required-permission: documents.delete
      x-permission-scope: index
      x-permission-roles:
      - writer
      - admin
      x-permission-anonymous: false
  /v1alpha1/indexes/{name}/facets/{field}/values:
    post:
      summary: Search the values of a facet
      description: |-
        Answers the values of one facet field that start with a prefix, each with how many documents hold it under the given query and filters. A filter panel asks for this while a value is typed into it, to reach the values a facet of a search cut off at its limit. The counts are the ones a facet of the same search answers: filter entries on the facet's own field are left out, and the query and every other filter narrow them.

        The prefix and the values of a string field are compared folded, in case and Unicode form, so `rö` finds `Röd`. A number, boolean or timestamp field compares the prefix with the value as a search response shows it, ignoring case. A field whose values are paths through a tree refuses a prefix. See [Searching the values of a facet](https://exofind.dev/reference/search-api/#searching-the-values-of-a-facet).

        Requires the `search` permission on the index the path names. The `reader`, `writer` and `admin` roles include it. A node that sets an anonymous key serves this endpoint to requests that carry no credential.
      operationId: searchFacetValues
      tags:
      - Search
      parameters:
      - description: "The field whose values to answer, as declared in the index definition.\
          \ The field must have `facet` enabled."
        example: brand
        name: field
        in: path
        required: true
        schema:
          type: string
      - description: "Name of the index. To count one generation, add `@` and the\
          \ name of the generation, such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "A freshness token, for a request that carries none in its body.\
          \ The body's `freshness.atLeast` is read when both are given."
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      requestBody:
        content:
          application/json:
            examples:
              values:
                summary: Brands starting with `adi` among running shoes
                value:
                  query:
                  - type: text
                    text: running shoes
                  filters:
                  - field: brand
                    match:
                      type: in
                      values:
                      - Nike
                  prefix: adi
                  limit: 5
            schema:
              $ref: "#/components/schemas/FacetValuesRequest"
        required: true
      responses:
        "200":
          description: "The values that start with the prefix, in the order that `order`\
            \ asks for."
          content:
            application/json:
              examples:
                values:
                  summary: The answer to the example request
                  value:
                    values:
                    - value: adidas
                      count: 87
                    - value: Adidas Originals
                      count: 12
                    totalValues: 2
                    generation: "2"
                    tookMs: 1.208
              schema:
                $ref: "#/components/schemas/FacetValuesResponse"
        "400":
          description: |-
            The request is not one that can be counted, or it asks for more than the node allows. See [Search configuration](https://exofind.dev/reference/configuration/#search) for the caps.

            Error codes: `request:value_required` - A part of the request that needs a value carries none. The `path` names it. `search:clause:field_required` - A `field` clause does not name the field to match. `search:clause:match_required` - A `field` clause does not say what to look for in the field. `search:clause:text_required` - A `text` clause carries no text to search for. `search:clause:path_required` - A `nested` clause does not name the object field to match inside. `search:clause:vector_required` - A `knn` clause carries no vector to find the neighbours of. `search:clause:k_out_of_range` - The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`. `search:clause:weight_out_of_range` - The `weight` of a `boost` clause is missing, below zero or not a finite number. `search:clause:slop_out_of_range` - The `slop` of a `text` clause is below zero. `search:clause:slop_unsupported` - A `text` clause sets `slop` without matching as a phrase. Set `match` to `phrase`, or to `user`. `search:clause:join_unsupported` - A `text` clause sets `join` without matching what somebody typed. Set `match` to `user`, or say `all` or `any` in `match` itself. `search:clause:rankings_too_few` - A `fuse` clause holds fewer than two rankings to fuse. `search:clause:ranking_empty` - A ranking of a `fuse` clause holds nothing to rank by. `search:clause:rank_constant_out_of_range` - The `rankConstant` of a `fuse` clause is not a number above zero. `search:clause:depth_out_of_range` - The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`. `search:clause:interpret_fields_required` - The `interpret` of a `text` clause names no target field. Name at least one, or use `auto` or `off`. `search:clause:interpret_when_unsupported` - The `when` of an `interpret` target holds a `nested`, `knn` or `fuse` clause. `search:matcher:value_required` - A matcher carries no value to look for. `search:matcher:range_empty` - A range matcher carries no bound. `search:matcher:range_conflicting` - A range matcher combines `gte` with `gt`, or `lte` with `lt`. `search:matcher:origin_required` - A distance matcher carries no `lat` and `lon` to measure from. `search:matcher:radius_required` - A distance matcher does not say how far from the origin values may be. `search:filter:clause_unsupported` - A clause under `filters` is neither a `field` nor a `nested` clause. A clause that scopes the whole search belongs in `query`. `search:locale_unsupported` - The request names a locale this engine has no rules for. `search:field_unknown` - The request names a field the index does not have. `search:usage_unsupported` - The field is not defined for `facet` usage. `search:facet:prefix_unsupported` - A prefix was given for a field whose values are paths through a tree. `search:facet:limit_out_of_range` - `limit` is below 1 or above `EXOFIND_SEARCH_MAX_FACET_VALUES`. The `max` argument carries the cap. `search:filter:scoring_unsupported` - A clause under `filter` affects the score. Move it out of `filter`. `search:clauses_too_many` - The query holds more clauses than the node allows. `search:clauses_too_deep` - The query nests deeper than the node allows. `search:value_invalid` - A matcher is given a value of the wrong kind for the type of its field. `search:matcher:type_unsupported` - A matcher is used on a field whose type cannot answer it. `search:no_searchable_fields` - A text clause names no fields and the index has none defined for matching. `search:nested:path_not_nested` - A `nested` clause names a path whose values are flattened. `search:nested:field_not_inside` - A clause inside a `nested` clause names a field outside its path. `search:nested:field_outside` - A clause outside a `nested` clause names a field inside a nested list. `search:nested:clause_unsupported` - A `nested` clause holds a clause that cannot run against a single value, such as `fuse`. `search:interpret:unit_required` - An `interpret` target names a field that is not a number field or declares no `unit`. `search:interpret:fallback_unit_mismatch` - A `fallback` target declares another unit than the target it stands in for. `search:clause:k_required` - A `knn` clause carries no `k`. `search:freshness:invalid` - The freshness token is not one the engine issued. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:value_required
            when: A part of the request that needs a value carries none. The `path`
              names it.
          - code: search:clause:field_required
            when: A `field` clause does not name the field to match.
          - code: search:clause:match_required
            when: A `field` clause does not say what to look for in the field.
          - code: search:clause:text_required
            when: A `text` clause carries no text to search for.
          - code: search:clause:path_required
            when: A `nested` clause does not name the object field to match inside.
          - code: search:clause:vector_required
            when: A `knn` clause carries no vector to find the neighbours of.
          - code: search:clause:k_out_of_range
            when: "The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`."
          - code: search:clause:weight_out_of_range
            when: "The `weight` of a `boost` clause is missing, below zero or not\
              \ a finite number."
          - code: search:clause:slop_out_of_range
            when: The `slop` of a `text` clause is below zero.
          - code: search:clause:slop_unsupported
            when: "A `text` clause sets `slop` without matching as a phrase. Set `match`\
              \ to `phrase`, or to `user`."
          - code: search:clause:join_unsupported
            when: "A `text` clause sets `join` without matching what somebody typed.\
              \ Set `match` to `user`, or say `all` or `any` in `match` itself."
          - code: search:clause:rankings_too_few
            when: A `fuse` clause holds fewer than two rankings to fuse.
          - code: search:clause:ranking_empty
            when: A ranking of a `fuse` clause holds nothing to rank by.
          - code: search:clause:rank_constant_out_of_range
            when: The `rankConstant` of a `fuse` clause is not a number above zero.
          - code: search:clause:depth_out_of_range
            when: "The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`."
          - code: search:clause:interpret_fields_required
            when: "The `interpret` of a `text` clause names no target field. Name\
              \ at least one, or use `auto` or `off`."
          - code: search:clause:interpret_when_unsupported
            when: "The `when` of an `interpret` target holds a `nested`, `knn` or\
              \ `fuse` clause."
          - code: search:matcher:value_required
            when: A matcher carries no value to look for.
          - code: search:matcher:range_empty
            when: A range matcher carries no bound.
          - code: search:matcher:range_conflicting
            when: "A range matcher combines `gte` with `gt`, or `lte` with `lt`."
          - code: search:matcher:origin_required
            when: A distance matcher carries no `lat` and `lon` to measure from.
          - code: search:matcher:radius_required
            when: A distance matcher does not say how far from the origin values may
              be.
          - code: search:filter:clause_unsupported
            when: A clause under `filters` is neither a `field` nor a `nested` clause.
              A clause that scopes the whole search belongs in `query`.
          - code: search:locale_unsupported
            when: The request names a locale this engine has no rules for.
          - code: search:field_unknown
            when: The request names a field the index does not have.
          - code: search:usage_unsupported
            when: The field is not defined for `facet` usage.
          - code: search:facet:prefix_unsupported
            when: A prefix was given for a field whose values are paths through a
              tree.
          - code: search:facet:limit_out_of_range
            when: '`limit` is below 1 or above `EXOFIND_SEARCH_MAX_FACET_VALUES`.
              The `max` argument carries the cap.'
          - code: search:filter:scoring_unsupported
            when: A clause under `filter` affects the score. Move it out of `filter`.
          - code: search:clauses_too_many
            when: The query holds more clauses than the node allows.
          - code: search:clauses_too_deep
            when: The query nests deeper than the node allows.
          - code: search:value_invalid
            when: A matcher is given a value of the wrong kind for the type of its
              field.
          - code: search:matcher:type_unsupported
            when: A matcher is used on a field whose type cannot answer it.
          - code: search:no_searchable_fields
            when: A text clause names no fields and the index has none defined for
              matching.
          - code: search:nested:path_not_nested
            when: A `nested` clause names a path whose values are flattened.
          - code: search:nested:field_not_inside
            when: A clause inside a `nested` clause names a field outside its path.
          - code: search:nested:field_outside
            when: A clause outside a `nested` clause names a field inside a nested
              list.
          - code: search:nested:clause_unsupported
            when: "A `nested` clause holds a clause that cannot run against a single\
              \ value, such as `fuse`."
          - code: search:interpret:unit_required
            when: An `interpret` target names a field that is not a number field or
              declares no `unit`.
          - code: search:interpret:fallback_unit_mismatch
            when: A `fallback` target declares another unit than the target it stands
              in for.
          - code: search:clause:k_required
            when: A `knn` clause carries no `k`.
          - code: search:freshness:invalid
            when: The freshness token is not one the engine issued. Pass a token back
              unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `search` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `search` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `search` permission.
        "404":
          description: |-
            The index does not exist, or the key has no permissions on it. An index on which a key has no permissions returns this status as though it did not exist.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be searched right now.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The counting did not finish on the node.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:timeout` - Counting ran for longer than `EXOFIND_SEARCH_TIMEOUT`. What it collected is dropped, so narrow the search rather than sending it again. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:timeout
            when: "Counting ran for longer than `EXOFIND_SEARCH_TIMEOUT`. What it\
              \ collected is dropped, so narrow the search rather than sending it\
              \ again."
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      x-required-permission: search
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: true
  /v1alpha1/indexes/{name}/search:
    post:
      summary: Search an index
      description: |-
        Executes a search query against an index on the node that receives the request. A node that does not index the target answers from the generation it last pulled, so a recently indexed document may not appear yet.

        Requires the `search` permission on the index the path names. The `reader`, `writer` and `admin` roles include it. A node that sets an anonymous key serves this endpoint to requests that carry no credential.
      operationId: search
      tags:
      - Search
      parameters:
      - description: "Name of the index to search. To search one generation, add `@`\
          \ and the name of the generation, such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "A freshness token, for a request that carries none in its body.\
          \ The body's `freshness.atLeast` is read when both are given."
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      requestBody:
        content:
          application/json:
            examples:
              search:
                summary: "Text and a filter, counted by category"
                value:
                  query:
                  - type: text
                    text: silent spr
                    fields:
                      name: 3
                  - field: published
                    match:
                      value: true
                  filters:
                  - field: category
                    match:
                      type: in
                      values:
                      - fiction
                      - poetry
                  facets:
                  - field: category
                  sort:
                  - type: score
                  - field: name
                    order: asc
                  fields:
                  - name
                  - price
                  limit: 20
            schema:
              $ref: "#/components/schemas/SearchRequest"
        required: true
      responses:
        "200":
          description: "The matching documents, in the order that `sort` asks for."
          content:
            application/json:
              examples:
                results:
                  summary: The answer to the example request
                  value:
                    hits:
                    - id: "9781234567890"
                      score: 8.42
                      document:
                        name: Silent Spring
                        price: 12.5
                    - id: "9780007458424"
                      score: 3.17
                      document:
                        name: Spring Snow
                        price: 9.95
                    total:
                      count: 128
                      exact: true
                    facets:
                      category:
                        values:
                        - value: fiction
                          count: 87
                        - value: poetry
                          count: 41
                        totalValues: 2
                    page:
                      limit: 20
                      offset: 0
                      next: AWtaPJHiAAAS1QFmQErhSA
                    generation: "2"
                    tookMs: 7.412
              schema:
                $ref: "#/components/schemas/SearchResponse"
        "400":
          description: |-
            The request is not a valid search, or it asks for more than the node allows. See [Search configuration](https://exofind.dev/reference/configuration/#search) for the caps.

            Error codes: `request:value_required` - A part of the request that needs a value carries none. The `path` names it. `search:clause:field_required` - A `field` clause does not name the field to match. `search:clause:match_required` - A `field` clause does not say what to look for in the field. `search:clause:text_required` - A `text` clause carries no text to search for. `search:clause:path_required` - A `nested` clause does not name the object field to match inside. `search:clause:vector_required` - A `knn` clause carries no vector to find the neighbours of. `search:clause:k_out_of_range` - The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`. `search:clause:weight_out_of_range` - The `weight` of a `boost` clause is missing, below zero or not a finite number. `search:clause:slop_out_of_range` - The `slop` of a `text` clause is below zero. `search:clause:slop_unsupported` - A `text` clause sets `slop` without matching as a phrase. Set `match` to `phrase`, or to `user`. `search:clause:join_unsupported` - A `text` clause sets `join` without matching what somebody typed. Set `match` to `user`, or say `all` or `any` in `match` itself. `search:clause:rankings_too_few` - A `fuse` clause holds fewer than two rankings to fuse. `search:clause:ranking_empty` - A ranking of a `fuse` clause holds nothing to rank by. `search:clause:rank_constant_out_of_range` - The `rankConstant` of a `fuse` clause is not a number above zero. `search:clause:depth_out_of_range` - The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`. `search:clause:interpret_fields_required` - The `interpret` of a `text` clause names no target field. Name at least one, or use `auto` or `off`. `search:clause:interpret_when_unsupported` - The `when` of an `interpret` target holds a `nested`, `knn` or `fuse` clause. `search:matcher:value_required` - A matcher carries no value to look for. `search:matcher:range_empty` - A range matcher carries no bound. `search:matcher:range_conflicting` - A range matcher combines `gte` with `gt`, or `lte` with `lt`. `search:matcher:origin_required` - A distance matcher carries no `lat` and `lon` to measure from. `search:matcher:radius_required` - A distance matcher does not say how far from the origin values may be. `search:filter:clause_unsupported` - A clause under `filters` is neither a `field` nor a `nested` clause. A clause that scopes the whole search belongs in `query`. `search:sort:field_required` - A `sort` entry does not name the field to sort by. `search:sort:origin_required` - A `sort` entry that orders by distance carries no `lat` and `lon` to measure from. `search:limit_out_of_range` - `limit` is below zero or above `EXOFIND_SEARCH_MAX_LIMIT`. `search:offset_out_of_range` - `offset` is below zero. `search:paging_conflicting` - The request combines more than one of `offset`, `after` and `before`. `search:cursor:invalid` - `after` or `before` carries a cursor this engine did not hand out. `search:cursor:sort_mismatch` - A cursor is used under a different `sort` than the one it was handed out under. Start the search again from the first page. `search:cursor:stale` - A cursor was taken under this `sort` but does not name a position in it, which a `sort` field that changed type in the index definition leaves behind. Start the search again from the first page. `search:pages:without_limit` - `pages` is asked for without a `limit` above zero. `search:pages:without_offset` - `pages` is asked for from a `next` or `previous` cursor, which carries no page number. Start from `offset` or from a page's own cursor. `search:pages:max_out_of_range` - The `max` of `pages` is not above zero. `search:facet:field_required` - A facet does not name the field to count. `search:facet:name_invalid` - A facet is keyed by a blank name. Leave `name` out to key the counts by the field. `search:facet:name_duplicate` - Two facets are keyed by the same name. `search:facet:limit_out_of_range` - The `limit` of a facet asks for more values than the node allows, or for none. The `max` argument carries the cap. `search:facet:depth_out_of_range` - The `depth` of a facet counts more levels of a tree than the node allows, or none. The `max` argument carries the cap. `search:facet:path_invalid` - The `path` of a facet is blank. Leave it out to count from the top of the tree. `search:facet:range_empty` - A bucket of a facet carries no bound. `search:facet:ranges_required` - A facet counts into buckets and lists none. Leave `ranges` out to count one value at a time. `search:facet:ranges_too_many` - A facet counts into more buckets than the node allows. The `max` argument carries the cap. `search:facet:ranges_conflicting` - A facet combines `ranges` with `limit` or `order`. A facet with `ranges` answers one count per bucket, in the order the buckets are given. `search:facet:ranges_with_tree` - A facet combines `ranges` with `path` or `depth`. Those count one level of a tree, and `ranges` counts buckets. `search:facet:exclude_filters_invalid` - An entry of `excludeFilters` is blank. Leave the list out for the facet's own field, or give it empty to leave nothing out. `search:highlight:fields_required` - `highlight` names no field to highlight. `search:highlight:field_required` - An entry of the fields to highlight is blank. `search:highlight:fragments_out_of_range` - The number of fragments to highlight is not above zero. `search:highlight:length_out_of_range` - The length a highlighted fragment aims for is outside 1 to 10000 characters. `search:matched:fields_required` - `matched` names no object field to answer matched values for. `search:matched:field_required` - An entry of the fields to answer matched values for is blank. `search:matched:fields_empty` - A `matched` entry asks for only some fields of the values and names none. `search:matched:field_not_inside` - A `matched` entry names a field that is not inside its object field. Name the fields of the values by their dotted paths. `search:matched:limit_out_of_range` - A `matched` entry asks for more values per field than the node allows, or for none. The `max` argument carries the cap. `search:hits:path_required` - `hits` does not name the object field whose matched values are the hits. `search:hits:fields_empty` - `hits` asks for only some fields of the values and names none. `search:hits:field_not_inside` - `hits` names a field that is not inside the object field it stands for. Name the fields of the values by their dotted paths. `search:hits:when_clause_unsupported` - A clause under `hits.when` is neither a `field` nor a `nested` clause. A clause that scopes the whole search belongs in `query`. `search:hits:when_scoring_unsupported` - A clause under `hits.when` affects the score. Clauses that score belong in `query`. `search:hits:when_sort_unsupported` - A search whose hits are chosen by `hits.when` is ordered by a field. Order it by score. `search:hits:sort_unsupported` - A search whose hits are the values of an object field is ordered by distance. `search:hits:knn_unsupported` - A search whose hits are the values of an object field holds a `knn` clause. `search:hits:matched_unsupported` - A search whose hits are the values of an object field also asks for `matched`, which would ask a hit about itself. `search:hits:highlight_field_not_inside` - A search whose hits are the values of an object field highlights a field that is not inside those values. `search:signal:field_required` - A signal does not name the field to read its value from. `search:signal:shape_invalid` - A signal is not exactly one of `saturation`, `decay` and `linear`. `search:signal:pivot_out_of_range` - The `pivot` of a saturation signal is not a number above zero. `search:signal:half_life_out_of_range` - The `halfLife` of a decay signal is not a number of seconds above zero. `search:signal:ceiling_out_of_range` - The `ceiling` of a linear signal is not a number above zero. `search:signal:weight_out_of_range` - The `weight` of a signal is below zero. `search:signal:mode_without_signals` - `signalsMode` is given without `signals`. Give the signals as well, or leave the mode out. `search:rescore:window_required` - A `rescore` block does not say how many of the best results the second pass reaches. `search:rescore:window_out_of_range` - The `window` of a `rescore` block is below one or above `EXOFIND_SEARCH_MAX_RESCORE_WINDOW`. `search:rescore:window_too_small` - `offset` plus `limit` reaches past the `window` of a `rescore` block. Widen the window, or ask for an earlier page. `search:rescore:empty` - A `rescore` block holds neither a boost nor a signal to reorder by. `search:rescore:weight_out_of_range` - The `weight` of a `rescore` block is below zero. `search:rescore:hits_unsupported` - A search whose hits are the values of an object field also asks to `rescore`. A second pass scores documents, so it cannot reorder values. `search:locale_unsupported` - The request names a locale this engine has no rules for. `search:source_not_kept` - `fields` asks for something only the document copy can answer on an index whose `source` is `none`. `search:interpret:unit_required` - An `interpret` target names a field that is not a number field or that declares no `unit`. `search:filter:scoring_unsupported` - A clause under `filter` affects the score. Move it out of `filter`. `search:field_unknown` - A clause names a field the index does not have. `search:usage_unsupported` - A field is not defined for the usage the clause asks of it. `search:paging_too_deep` - `offset` plus `limit` reaches past `EXOFIND_SEARCH_MAX_PAGE_DEPTH`. Follow the `next` cursor instead. `search:clauses_too_many` - The query holds more clauses than the node allows. `search:clauses_too_deep` - The query nests deeper than the node allows. `search:value_invalid` - A matcher is given a value of the wrong kind for the type of its field. `search:matcher:type_unsupported` - A matcher is used on a field whose type cannot answer it. `search:no_searchable_fields` - A text clause names no fields and the index has none defined for matching. `search:nested:path_not_nested` - A `nested` clause names a path whose values are flattened. `search:nested:field_not_inside` - A clause inside a `nested` clause names a field outside its path. `search:nested:field_outside` - A clause outside a `nested` clause names a field inside a nested list. `search:nested:clause_unsupported` - A `nested` clause holds a clause that cannot run against a single value, such as `fuse`. `search:interpret:fallback_unit_mismatch` - A `fallback` target declares another unit than the target it stands in for. `search:facet:range_invalid` - A range bucket has `to` at or below `from`. `search:hits:facet_unsupported` - A facet of a search with `hits` names a field inside another object than the `hits` path. `search:hits:path_not_nested` - The `hits` path names an object field that is not in `nested` mode. `search:matched:field_not_nested` - A `matched` field is an object field that is not in `nested` mode. `search:sort:nested_unsupported` - A sort names a field inside a nested list in a way its values cannot be ordered. `search:clause:k_required` - A `knn` clause carries no `k`. `search:freshness:invalid` - The freshness token is not one the engine issued. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:value_required
            when: A part of the request that needs a value carries none. The `path`
              names it.
          - code: search:clause:field_required
            when: A `field` clause does not name the field to match.
          - code: search:clause:match_required
            when: A `field` clause does not say what to look for in the field.
          - code: search:clause:text_required
            when: A `text` clause carries no text to search for.
          - code: search:clause:path_required
            when: A `nested` clause does not name the object field to match inside.
          - code: search:clause:vector_required
            when: A `knn` clause carries no vector to find the neighbours of.
          - code: search:clause:k_out_of_range
            when: "The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`."
          - code: search:clause:weight_out_of_range
            when: "The `weight` of a `boost` clause is missing, below zero or not\
              \ a finite number."
          - code: search:clause:slop_out_of_range
            when: The `slop` of a `text` clause is below zero.
          - code: search:clause:slop_unsupported
            when: "A `text` clause sets `slop` without matching as a phrase. Set `match`\
              \ to `phrase`, or to `user`."
          - code: search:clause:join_unsupported
            when: "A `text` clause sets `join` without matching what somebody typed.\
              \ Set `match` to `user`, or say `all` or `any` in `match` itself."
          - code: search:clause:rankings_too_few
            when: A `fuse` clause holds fewer than two rankings to fuse.
          - code: search:clause:ranking_empty
            when: A ranking of a `fuse` clause holds nothing to rank by.
          - code: search:clause:rank_constant_out_of_range
            when: The `rankConstant` of a `fuse` clause is not a number above zero.
          - code: search:clause:depth_out_of_range
            when: "The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`."
          - code: search:clause:interpret_fields_required
            when: "The `interpret` of a `text` clause names no target field. Name\
              \ at least one, or use `auto` or `off`."
          - code: search:clause:interpret_when_unsupported
            when: "The `when` of an `interpret` target holds a `nested`, `knn` or\
              \ `fuse` clause."
          - code: search:matcher:value_required
            when: A matcher carries no value to look for.
          - code: search:matcher:range_empty
            when: A range matcher carries no bound.
          - code: search:matcher:range_conflicting
            when: "A range matcher combines `gte` with `gt`, or `lte` with `lt`."
          - code: search:matcher:origin_required
            when: A distance matcher carries no `lat` and `lon` to measure from.
          - code: search:matcher:radius_required
            when: A distance matcher does not say how far from the origin values may
              be.
          - code: search:filter:clause_unsupported
            when: A clause under `filters` is neither a `field` nor a `nested` clause.
              A clause that scopes the whole search belongs in `query`.
          - code: search:sort:field_required
            when: A `sort` entry does not name the field to sort by.
          - code: search:sort:origin_required
            when: A `sort` entry that orders by distance carries no `lat` and `lon`
              to measure from.
          - code: search:limit_out_of_range
            when: '`limit` is below zero or above `EXOFIND_SEARCH_MAX_LIMIT`.'
          - code: search:offset_out_of_range
            when: '`offset` is below zero.'
          - code: search:paging_conflicting
            when: "The request combines more than one of `offset`, `after` and `before`."
          - code: search:cursor:invalid
            when: '`after` or `before` carries a cursor this engine did not hand out.'
          - code: search:cursor:sort_mismatch
            when: A cursor is used under a different `sort` than the one it was handed
              out under. Start the search again from the first page.
          - code: search:cursor:stale
            when: "A cursor was taken under this `sort` but does not name a position\
              \ in it, which a `sort` field that changed type in the index definition\
              \ leaves behind. Start the search again from the first page."
          - code: search:pages:without_limit
            when: '`pages` is asked for without a `limit` above zero.'
          - code: search:pages:without_offset
            when: "`pages` is asked for from a `next` or `previous` cursor, which\
              \ carries no page number. Start from `offset` or from a page's own cursor."
          - code: search:pages:max_out_of_range
            when: The `max` of `pages` is not above zero.
          - code: search:facet:field_required
            when: A facet does not name the field to count.
          - code: search:facet:name_invalid
            when: A facet is keyed by a blank name. Leave `name` out to key the counts
              by the field.
          - code: search:facet:name_duplicate
            when: Two facets are keyed by the same name.
          - code: search:facet:limit_out_of_range
            when: "The `limit` of a facet asks for more values than the node allows,\
              \ or for none. The `max` argument carries the cap."
          - code: search:facet:depth_out_of_range
            when: "The `depth` of a facet counts more levels of a tree than the node\
              \ allows, or none. The `max` argument carries the cap."
          - code: search:facet:path_invalid
            when: The `path` of a facet is blank. Leave it out to count from the top
              of the tree.
          - code: search:facet:range_empty
            when: A bucket of a facet carries no bound.
          - code: search:facet:ranges_required
            when: A facet counts into buckets and lists none. Leave `ranges` out to
              count one value at a time.
          - code: search:facet:ranges_too_many
            when: A facet counts into more buckets than the node allows. The `max`
              argument carries the cap.
          - code: search:facet:ranges_conflicting
            when: "A facet combines `ranges` with `limit` or `order`. A facet with\
              \ `ranges` answers one count per bucket, in the order the buckets are\
              \ given."
          - code: search:facet:ranges_with_tree
            when: "A facet combines `ranges` with `path` or `depth`. Those count one\
              \ level of a tree, and `ranges` counts buckets."
          - code: search:facet:exclude_filters_invalid
            when: "An entry of `excludeFilters` is blank. Leave the list out for the\
              \ facet's own field, or give it empty to leave nothing out."
          - code: search:highlight:fields_required
            when: '`highlight` names no field to highlight.'
          - code: search:highlight:field_required
            when: An entry of the fields to highlight is blank.
          - code: search:highlight:fragments_out_of_range
            when: The number of fragments to highlight is not above zero.
          - code: search:highlight:length_out_of_range
            when: The length a highlighted fragment aims for is outside 1 to 10000
              characters.
          - code: search:matched:fields_required
            when: '`matched` names no object field to answer matched values for.'
          - code: search:matched:field_required
            when: An entry of the fields to answer matched values for is blank.
          - code: search:matched:fields_empty
            when: A `matched` entry asks for only some fields of the values and names
              none.
          - code: search:matched:field_not_inside
            when: A `matched` entry names a field that is not inside its object field.
              Name the fields of the values by their dotted paths.
          - code: search:matched:limit_out_of_range
            when: "A `matched` entry asks for more values per field than the node\
              \ allows, or for none. The `max` argument carries the cap."
          - code: search:hits:path_required
            when: '`hits` does not name the object field whose matched values are
              the hits.'
          - code: search:hits:fields_empty
            when: '`hits` asks for only some fields of the values and names none.'
          - code: search:hits:field_not_inside
            when: '`hits` names a field that is not inside the object field it stands
              for. Name the fields of the values by their dotted paths.'
          - code: search:hits:when_clause_unsupported
            when: A clause under `hits.when` is neither a `field` nor a `nested` clause.
              A clause that scopes the whole search belongs in `query`.
          - code: search:hits:when_scoring_unsupported
            when: A clause under `hits.when` affects the score. Clauses that score
              belong in `query`.
          - code: search:hits:when_sort_unsupported
            when: A search whose hits are chosen by `hits.when` is ordered by a field.
              Order it by score.
          - code: search:hits:sort_unsupported
            when: A search whose hits are the values of an object field is ordered
              by distance.
          - code: search:hits:knn_unsupported
            when: A search whose hits are the values of an object field holds a `knn`
              clause.
          - code: search:hits:matched_unsupported
            when: "A search whose hits are the values of an object field also asks\
              \ for `matched`, which would ask a hit about itself."
          - code: search:hits:highlight_field_not_inside
            when: A search whose hits are the values of an object field highlights
              a field that is not inside those values.
          - code: search:signal:field_required
            when: A signal does not name the field to read its value from.
          - code: search:signal:shape_invalid
            when: "A signal is not exactly one of `saturation`, `decay` and `linear`."
          - code: search:signal:pivot_out_of_range
            when: The `pivot` of a saturation signal is not a number above zero.
          - code: search:signal:half_life_out_of_range
            when: The `halfLife` of a decay signal is not a number of seconds above
              zero.
          - code: search:signal:ceiling_out_of_range
            when: The `ceiling` of a linear signal is not a number above zero.
          - code: search:signal:weight_out_of_range
            when: The `weight` of a signal is below zero.
          - code: search:signal:mode_without_signals
            when: "`signalsMode` is given without `signals`. Give the signals as well,\
              \ or leave the mode out."
          - code: search:rescore:window_required
            when: A `rescore` block does not say how many of the best results the
              second pass reaches.
          - code: search:rescore:window_out_of_range
            when: The `window` of a `rescore` block is below one or above `EXOFIND_SEARCH_MAX_RESCORE_WINDOW`.
          - code: search:rescore:window_too_small
            when: "`offset` plus `limit` reaches past the `window` of a `rescore`\
              \ block. Widen the window, or ask for an earlier page."
          - code: search:rescore:empty
            when: A `rescore` block holds neither a boost nor a signal to reorder
              by.
          - code: search:rescore:weight_out_of_range
            when: The `weight` of a `rescore` block is below zero.
          - code: search:rescore:hits_unsupported
            when: "A search whose hits are the values of an object field also asks\
              \ to `rescore`. A second pass scores documents, so it cannot reorder\
              \ values."
          - code: search:locale_unsupported
            when: The request names a locale this engine has no rules for.
          - code: search:source_not_kept
            when: '`fields` asks for something only the document copy can answer on
              an index whose `source` is `none`.'
          - code: search:interpret:unit_required
            when: An `interpret` target names a field that is not a number field or
              that declares no `unit`.
          - code: search:filter:scoring_unsupported
            when: A clause under `filter` affects the score. Move it out of `filter`.
          - code: search:field_unknown
            when: A clause names a field the index does not have.
          - code: search:usage_unsupported
            when: A field is not defined for the usage the clause asks of it.
          - code: search:paging_too_deep
            when: '`offset` plus `limit` reaches past `EXOFIND_SEARCH_MAX_PAGE_DEPTH`.
              Follow the `next` cursor instead.'
          - code: search:clauses_too_many
            when: The query holds more clauses than the node allows.
          - code: search:clauses_too_deep
            when: The query nests deeper than the node allows.
          - code: search:value_invalid
            when: A matcher is given a value of the wrong kind for the type of its
              field.
          - code: search:matcher:type_unsupported
            when: A matcher is used on a field whose type cannot answer it.
          - code: search:no_searchable_fields
            when: A text clause names no fields and the index has none defined for
              matching.
          - code: search:nested:path_not_nested
            when: A `nested` clause names a path whose values are flattened.
          - code: search:nested:field_not_inside
            when: A clause inside a `nested` clause names a field outside its path.
          - code: search:nested:field_outside
            when: A clause outside a `nested` clause names a field inside a nested
              list.
          - code: search:nested:clause_unsupported
            when: "A `nested` clause holds a clause that cannot run against a single\
              \ value, such as `fuse`."
          - code: search:interpret:fallback_unit_mismatch
            when: A `fallback` target declares another unit than the target it stands
              in for.
          - code: search:facet:range_invalid
            when: A range bucket has `to` at or below `from`.
          - code: search:hits:facet_unsupported
            when: A facet of a search with `hits` names a field inside another object
              than the `hits` path.
          - code: search:hits:path_not_nested
            when: The `hits` path names an object field that is not in `nested` mode.
          - code: search:matched:field_not_nested
            when: A `matched` field is an object field that is not in `nested` mode.
          - code: search:sort:nested_unsupported
            when: A sort names a field inside a nested list in a way its values cannot
              be ordered.
          - code: search:clause:k_required
            when: A `knn` clause carries no `k`.
          - code: search:freshness:invalid
            when: The freshness token is not one the engine issued. Pass a token back
              unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `search` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `search` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `search` permission.
        "404":
          description: |-
            The index does not exist, or the key has no permissions on it. An index on which a key has no permissions returns this status as though it did not exist.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be searched right now.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The search did not finish on the node.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:timeout` - The search collected for longer than `EXOFIND_SEARCH_TIMEOUT`. What it collected is dropped, so narrow the search rather than sending it again. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:timeout
            when: "The search collected for longer than `EXOFIND_SEARCH_TIMEOUT`.\
              \ What it collected is dropped, so narrow the search rather than sending\
              \ it again."
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      x-required-permission: search
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: true
  /v1alpha1/indexes/{name}/search/actions/explain:
    post:
      summary: Explain how one hit scores
      description: |-
        Reports how one hit scores under a search, as a tree of steps naming the clauses of the request they were compiled from and the fields of the index definition they read.

        The body is a search request; `limit`, `offset`, `after`, `before`, `pages`, `total`, `sort`, `facets`, `highlight`, `matched`, `fields` and `rescore` are ignored, and are not checked either, so a cursor taken under another sort or an offset past what the node allows still gets an answer. A hit that the search does not match is reported with `matched` set to `false` rather than refused.

        Requires the `search` permission on the index the path names. The `reader`, `writer` and `admin` roles include it. A node that sets an anonymous key serves this endpoint to requests that carry no credential.
      operationId: explainSearch
      tags:
      - Search
      parameters:
      - description: "Name of the index to search. To explain against one generation,\
          \ add `@` and the name of the generation, such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "Position of the value to explain among the document's values\
          \ of the `hits` path, which is what such a hit reports as its `index`. Read\
          \ only by a search whose hits are values."
        example: 0
        name: index
        in: query
        schema:
          type: integer
          format: int32
          default: 0
      - description: "Primary key of the document, read as the type of the key field.\
          \ For a search whose `hits` names an object field, provide the key of the\
          \ document holding the value, which is what such a hit reports as its `id`."
        example: "9781234567890"
        required: true
        name: key
        in: query
        schema:
          type: string
      - description: "A freshness token, for a request that carries none in its body.\
          \ The body's `freshness.atLeast` is read when both are given."
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      requestBody:
        content:
          application/json:
            examples:
              search:
                summary: The search to explain the hit against
                value:
                  query:
                  - type: text
                    text: silent spr
                    fields:
                      name: 3
                  - field: published
                    match:
                      value: true
                  filters:
                  - field: category
                    match:
                      type: in
                      values:
                      - fiction
                      - poetry
                  facets:
                  - field: category
                  sort:
                  - type: score
                  - field: name
                    order: asc
                  fields:
                  - name
                  - price
                  limit: 20
            schema:
              $ref: "#/components/schemas/SearchRequest"
        required: true
      responses:
        "200":
          description: How the hit scores.
          content:
            application/json:
              examples:
                explanation:
                  value:
                    matched: true
                    score: 8.42
                    detail:
                      matched: true
                      score: 8.42
                      description: "sum of:"
                      children:
                      - matched: true
                        score: 8.42
                        description: "weight(name:spring) [BM25], result of:"
                        clause: "query[0]"
                        clauseType: text
                        field: name
                        usage: matching
                    generation: "2"
                    tookMs: 1.208
              schema:
                $ref: "#/components/schemas/ExplainResponse"
        "400":
          description: |-
            The request is not a valid search, or the index declares no primary key so a hit cannot be named.

            Error codes: `request:value_required` - A part of the request that needs a value carries none. The `path` names it. `search:clause:field_required` - A `field` clause does not name the field to match. `search:clause:match_required` - A `field` clause does not say what to look for in the field. `search:clause:text_required` - A `text` clause carries no text to search for. `search:clause:path_required` - A `nested` clause does not name the object field to match inside. `search:clause:vector_required` - A `knn` clause carries no vector to find the neighbours of. `search:clause:k_out_of_range` - The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`. `search:clause:weight_out_of_range` - The `weight` of a `boost` clause is missing, below zero or not a finite number. `search:clause:slop_out_of_range` - The `slop` of a `text` clause is below zero. `search:clause:slop_unsupported` - A `text` clause sets `slop` without matching as a phrase. Set `match` to `phrase`, or to `user`. `search:clause:join_unsupported` - A `text` clause sets `join` without matching what somebody typed. Set `match` to `user`, or say `all` or `any` in `match` itself. `search:clause:rankings_too_few` - A `fuse` clause holds fewer than two rankings to fuse. `search:clause:ranking_empty` - A ranking of a `fuse` clause holds nothing to rank by. `search:clause:rank_constant_out_of_range` - The `rankConstant` of a `fuse` clause is not a number above zero. `search:clause:depth_out_of_range` - The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`. `search:clause:interpret_fields_required` - The `interpret` of a `text` clause names no target field. Name at least one, or use `auto` or `off`. `search:clause:interpret_when_unsupported` - The `when` of an `interpret` target holds a `nested`, `knn` or `fuse` clause. `search:matcher:value_required` - A matcher carries no value to look for. `search:matcher:range_empty` - A range matcher carries no bound. `search:matcher:range_conflicting` - A range matcher combines `gte` with `gt`, or `lte` with `lt`. `search:matcher:origin_required` - A distance matcher carries no `lat` and `lon` to measure from. `search:matcher:radius_required` - A distance matcher does not say how far from the origin values may be. `search:filter:clause_unsupported` - A clause under `filters` is neither a `field` nor a `nested` clause. A clause that scopes the whole search belongs in `query`. `search:filter:scoring_unsupported` - A clause under `filters` affects the score. Move it out of `filters`. `search:sort:field_required` - A `sort` entry does not name the field to sort by. `search:sort:origin_required` - A `sort` entry that orders by distance carries no `lat` and `lon` to measure from. `search:limit_out_of_range` - `limit` is below zero or above `EXOFIND_SEARCH_MAX_LIMIT`. `search:offset_out_of_range` - `offset` is below zero. `search:paging_conflicting` - The request combines more than one of `offset`, `after` and `before`. `search:paging_too_deep` - `offset` plus `limit` reaches past `EXOFIND_SEARCH_MAX_PAGE_DEPTH`. `search:cursor:invalid` - `after` or `before` carries a cursor this engine did not hand out. `search:cursor:sort_mismatch` - A cursor is used under a different `sort` than the one it was handed out under. `search:pages:without_limit` - `pages` is asked for without a `limit` above zero. `search:pages:without_offset` - `pages` is asked for from a `next` or `previous` cursor, which carries no page number. `search:pages:max_out_of_range` - The `max` of `pages` is not above zero. `search:clauses_too_many` - The query holds more clauses than the node allows. `search:clauses_too_deep` - The query nests deeper than the node allows. `search:facet:field_required` - A facet does not name the field to count. `search:facet:name_invalid` - A facet is keyed by a blank name. Leave `name` out to key the counts by the field. `search:facet:name_duplicate` - Two facets are keyed by the same name. `search:facet:limit_out_of_range` - The `limit` of a facet asks for more values than the node allows, or for none. `search:facet:depth_out_of_range` - The `depth` of a facet counts more levels of a tree than the node allows, or none. `search:facet:path_invalid` - The `path` of a facet is blank. Leave it out to count from the top of the tree. `search:facet:range_empty` - A bucket of a facet carries no bound. `search:facet:ranges_required` - A facet counts into buckets and lists none. Leave `ranges` out to count one value at a time. `search:facet:ranges_too_many` - A facet counts into more buckets than the node allows. `search:facet:ranges_conflicting` - A facet combines `ranges` with `limit` or `order`. A facet with `ranges` answers one count per bucket, in the order the buckets are given. `search:facet:ranges_with_tree` - A facet combines `ranges` with `path` or `depth`. Those count one level of a tree, and `ranges` counts buckets. `search:facet:exclude_filters_invalid` - An entry of `excludeFilters` is blank. `search:highlight:fields_required` - `highlight` names no field to highlight. `search:highlight:field_required` - An entry of the fields to highlight is blank. `search:highlight:fragments_out_of_range` - The number of fragments to highlight is not above zero. `search:highlight:length_out_of_range` - The length a highlighted fragment aims for is outside 1 to 10000 characters. `search:matched:fields_required` - `matched` names no object field to answer matched values for. `search:matched:field_required` - An entry of the fields to answer matched values for is blank. `search:matched:fields_empty` - A `matched` entry asks for only some fields of the values and names none. `search:matched:field_not_inside` - A `matched` entry names a field that is not inside its object field. `search:matched:limit_out_of_range` - A `matched` entry asks for more values per field than the node allows, or for none. `search:hits:path_required` - `hits` does not name the object field whose matched values are the hits. `search:hits:fields_empty` - `hits` asks for only some fields of the values and names none. `search:hits:field_not_inside` - `hits` names a field that is not inside the object field it stands for. `search:hits:when_clause_unsupported` - A clause under `hits.when` is neither a `field` nor a `nested` clause. `search:hits:when_scoring_unsupported` - A clause under `hits.when` affects the score. Clauses that score belong in `query`. `search:hits:when_sort_unsupported` - A search whose hits are chosen by `hits.when` is ordered by a field. Order it by score. `search:hits:sort_unsupported` - A search whose hits are the values of an object field is ordered by distance. `search:hits:knn_unsupported` - A search whose hits are the values of an object field holds a `knn` clause. `search:hits:matched_unsupported` - A search whose hits are the values of an object field also asks for `matched`. `search:hits:highlight_field_not_inside` - A search whose hits are the values of an object field highlights a field that is not inside those values. `search:signal:field_required` - A signal does not name the field to read its value from. `search:signal:shape_invalid` - A signal is not exactly one of `saturation`, `decay` and `linear`. `search:signal:pivot_out_of_range` - The `pivot` of a saturation signal is not a number above zero. `search:signal:half_life_out_of_range` - The `halfLife` of a decay signal is not a number of seconds above zero. `search:signal:ceiling_out_of_range` - The `ceiling` of a linear signal is not a number above zero. `search:signal:weight_out_of_range` - The `weight` of a signal is below zero. `search:signal:mode_without_signals` - `signalsMode` is given without `signals`. `search:rescore:window_required` - A `rescore` block does not say how many of the best results the second pass reaches. `search:rescore:window_out_of_range` - The `window` of a `rescore` block is below one or above `EXOFIND_SEARCH_MAX_RESCORE_WINDOW`. `search:rescore:window_too_small` - `offset` plus `limit` reaches past the `window` of a `rescore` block. `search:rescore:empty` - A `rescore` block holds neither a boost nor a signal to reorder by. `search:rescore:weight_out_of_range` - The `weight` of a `rescore` block is below zero. `search:rescore:hits_unsupported` - A search whose hits are the values of an object field also asks to `rescore`. `search:locale_unsupported` - The request names a locale this engine has no rules for. `index:no_primary_key` - The index declares no primary key, so a hit cannot be named. `search:explain:key_required` - The request carries no `key`, so it names no hit to explain. `search:explain:index_out_of_range` - `index` is below zero, so it names no value of the `hits` path. `search:field_unknown` - A clause, sort or facet names a field the index does not have. `search:usage_unsupported` - A clause, sort or facet uses a field in a way the definition does not enable for it. `search:value_invalid` - A matcher is given a value of the wrong kind for the type of its field. `search:matcher:type_unsupported` - A matcher is used on a field whose type cannot answer it. `search:no_searchable_fields` - A text clause names no fields and the index has none defined for matching. `search:nested:path_not_nested` - A `nested` clause names a path whose values are flattened. `search:nested:field_not_inside` - A clause inside a `nested` clause names a field outside its path. `search:nested:field_outside` - A clause outside a `nested` clause names a field inside a nested list. `search:nested:clause_unsupported` - A `nested` clause holds a clause that cannot run against a single value, such as `fuse`. `search:interpret:unit_required` - An `interpret` target names a field that is not a number field or declares no `unit`. `search:interpret:fallback_unit_mismatch` - A `fallback` target declares another unit than the target it stands in for. `search:source_not_kept` - `fields` names something only the document copy can answer, and the index keeps none. `search:facet:range_invalid` - A range bucket has `to` at or below `from`. `search:hits:facet_unsupported` - A facet of a search with `hits` names a field inside another object than the `hits` path. `search:hits:path_not_nested` - The `hits` path names an object field that is not in `nested` mode. `search:matched:field_not_nested` - A `matched` field is an object field that is not in `nested` mode. `search:sort:nested_unsupported` - A sort names a field inside a nested list in a way its values cannot be ordered. `search:cursor:stale` - The cursor was taken under this sort, but the values it carries no longer fit how the index defines the sort. Start from the first page. `search:clause:k_required` - A `knn` clause carries no `k`. `search:freshness:invalid` - The freshness token is not one the engine issued. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: request:value_required
            when: A part of the request that needs a value carries none. The `path`
              names it.
          - code: search:clause:field_required
            when: A `field` clause does not name the field to match.
          - code: search:clause:match_required
            when: A `field` clause does not say what to look for in the field.
          - code: search:clause:text_required
            when: A `text` clause carries no text to search for.
          - code: search:clause:path_required
            when: A `nested` clause does not name the object field to match inside.
          - code: search:clause:vector_required
            when: A `knn` clause carries no vector to find the neighbours of.
          - code: search:clause:k_out_of_range
            when: "The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`."
          - code: search:clause:weight_out_of_range
            when: "The `weight` of a `boost` clause is missing, below zero or not\
              \ a finite number."
          - code: search:clause:slop_out_of_range
            when: The `slop` of a `text` clause is below zero.
          - code: search:clause:slop_unsupported
            when: "A `text` clause sets `slop` without matching as a phrase. Set `match`\
              \ to `phrase`, or to `user`."
          - code: search:clause:join_unsupported
            when: "A `text` clause sets `join` without matching what somebody typed.\
              \ Set `match` to `user`, or say `all` or `any` in `match` itself."
          - code: search:clause:rankings_too_few
            when: A `fuse` clause holds fewer than two rankings to fuse.
          - code: search:clause:ranking_empty
            when: A ranking of a `fuse` clause holds nothing to rank by.
          - code: search:clause:rank_constant_out_of_range
            when: The `rankConstant` of a `fuse` clause is not a number above zero.
          - code: search:clause:depth_out_of_range
            when: "The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`."
          - code: search:clause:interpret_fields_required
            when: "The `interpret` of a `text` clause names no target field. Name\
              \ at least one, or use `auto` or `off`."
          - code: search:clause:interpret_when_unsupported
            when: "The `when` of an `interpret` target holds a `nested`, `knn` or\
              \ `fuse` clause."
          - code: search:matcher:value_required
            when: A matcher carries no value to look for.
          - code: search:matcher:range_empty
            when: A range matcher carries no bound.
          - code: search:matcher:range_conflicting
            when: "A range matcher combines `gte` with `gt`, or `lte` with `lt`."
          - code: search:matcher:origin_required
            when: A distance matcher carries no `lat` and `lon` to measure from.
          - code: search:matcher:radius_required
            when: A distance matcher does not say how far from the origin values may
              be.
          - code: search:filter:clause_unsupported
            when: A clause under `filters` is neither a `field` nor a `nested` clause.
              A clause that scopes the whole search belongs in `query`.
          - code: search:filter:scoring_unsupported
            when: A clause under `filters` affects the score. Move it out of `filters`.
          - code: search:sort:field_required
            when: A `sort` entry does not name the field to sort by.
          - code: search:sort:origin_required
            when: A `sort` entry that orders by distance carries no `lat` and `lon`
              to measure from.
          - code: search:limit_out_of_range
            when: '`limit` is below zero or above `EXOFIND_SEARCH_MAX_LIMIT`.'
          - code: search:offset_out_of_range
            when: '`offset` is below zero.'
          - code: search:paging_conflicting
            when: "The request combines more than one of `offset`, `after` and `before`."
          - code: search:paging_too_deep
            when: '`offset` plus `limit` reaches past `EXOFIND_SEARCH_MAX_PAGE_DEPTH`.'
          - code: search:cursor:invalid
            when: '`after` or `before` carries a cursor this engine did not hand out.'
          - code: search:cursor:sort_mismatch
            when: A cursor is used under a different `sort` than the one it was handed
              out under.
          - code: search:pages:without_limit
            when: '`pages` is asked for without a `limit` above zero.'
          - code: search:pages:without_offset
            when: "`pages` is asked for from a `next` or `previous` cursor, which\
              \ carries no page number."
          - code: search:pages:max_out_of_range
            when: The `max` of `pages` is not above zero.
          - code: search:clauses_too_many
            when: The query holds more clauses than the node allows.
          - code: search:clauses_too_deep
            when: The query nests deeper than the node allows.
          - code: search:facet:field_required
            when: A facet does not name the field to count.
          - code: search:facet:name_invalid
            when: A facet is keyed by a blank name. Leave `name` out to key the counts
              by the field.
          - code: search:facet:name_duplicate
            when: Two facets are keyed by the same name.
          - code: search:facet:limit_out_of_range
            when: "The `limit` of a facet asks for more values than the node allows,\
              \ or for none."
          - code: search:facet:depth_out_of_range
            when: "The `depth` of a facet counts more levels of a tree than the node\
              \ allows, or none."
          - code: search:facet:path_invalid
            when: The `path` of a facet is blank. Leave it out to count from the top
              of the tree.
          - code: search:facet:range_empty
            when: A bucket of a facet carries no bound.
          - code: search:facet:ranges_required
            when: A facet counts into buckets and lists none. Leave `ranges` out to
              count one value at a time.
          - code: search:facet:ranges_too_many
            when: A facet counts into more buckets than the node allows.
          - code: search:facet:ranges_conflicting
            when: "A facet combines `ranges` with `limit` or `order`. A facet with\
              \ `ranges` answers one count per bucket, in the order the buckets are\
              \ given."
          - code: search:facet:ranges_with_tree
            when: "A facet combines `ranges` with `path` or `depth`. Those count one\
              \ level of a tree, and `ranges` counts buckets."
          - code: search:facet:exclude_filters_invalid
            when: An entry of `excludeFilters` is blank.
          - code: search:highlight:fields_required
            when: '`highlight` names no field to highlight.'
          - code: search:highlight:field_required
            when: An entry of the fields to highlight is blank.
          - code: search:highlight:fragments_out_of_range
            when: The number of fragments to highlight is not above zero.
          - code: search:highlight:length_out_of_range
            when: The length a highlighted fragment aims for is outside 1 to 10000
              characters.
          - code: search:matched:fields_required
            when: '`matched` names no object field to answer matched values for.'
          - code: search:matched:field_required
            when: An entry of the fields to answer matched values for is blank.
          - code: search:matched:fields_empty
            when: A `matched` entry asks for only some fields of the values and names
              none.
          - code: search:matched:field_not_inside
            when: A `matched` entry names a field that is not inside its object field.
          - code: search:matched:limit_out_of_range
            when: "A `matched` entry asks for more values per field than the node\
              \ allows, or for none."
          - code: search:hits:path_required
            when: '`hits` does not name the object field whose matched values are
              the hits.'
          - code: search:hits:fields_empty
            when: '`hits` asks for only some fields of the values and names none.'
          - code: search:hits:field_not_inside
            when: '`hits` names a field that is not inside the object field it stands
              for.'
          - code: search:hits:when_clause_unsupported
            when: A clause under `hits.when` is neither a `field` nor a `nested` clause.
          - code: search:hits:when_scoring_unsupported
            when: A clause under `hits.when` affects the score. Clauses that score
              belong in `query`.
          - code: search:hits:when_sort_unsupported
            when: A search whose hits are chosen by `hits.when` is ordered by a field.
              Order it by score.
          - code: search:hits:sort_unsupported
            when: A search whose hits are the values of an object field is ordered
              by distance.
          - code: search:hits:knn_unsupported
            when: A search whose hits are the values of an object field holds a `knn`
              clause.
          - code: search:hits:matched_unsupported
            when: A search whose hits are the values of an object field also asks
              for `matched`.
          - code: search:hits:highlight_field_not_inside
            when: A search whose hits are the values of an object field highlights
              a field that is not inside those values.
          - code: search:signal:field_required
            when: A signal does not name the field to read its value from.
          - code: search:signal:shape_invalid
            when: "A signal is not exactly one of `saturation`, `decay` and `linear`."
          - code: search:signal:pivot_out_of_range
            when: The `pivot` of a saturation signal is not a number above zero.
          - code: search:signal:half_life_out_of_range
            when: The `halfLife` of a decay signal is not a number of seconds above
              zero.
          - code: search:signal:ceiling_out_of_range
            when: The `ceiling` of a linear signal is not a number above zero.
          - code: search:signal:weight_out_of_range
            when: The `weight` of a signal is below zero.
          - code: search:signal:mode_without_signals
            when: '`signalsMode` is given without `signals`.'
          - code: search:rescore:window_required
            when: A `rescore` block does not say how many of the best results the
              second pass reaches.
          - code: search:rescore:window_out_of_range
            when: The `window` of a `rescore` block is below one or above `EXOFIND_SEARCH_MAX_RESCORE_WINDOW`.
          - code: search:rescore:window_too_small
            when: '`offset` plus `limit` reaches past the `window` of a `rescore`
              block.'
          - code: search:rescore:empty
            when: A `rescore` block holds neither a boost nor a signal to reorder
              by.
          - code: search:rescore:weight_out_of_range
            when: The `weight` of a `rescore` block is below zero.
          - code: search:rescore:hits_unsupported
            when: A search whose hits are the values of an object field also asks
              to `rescore`.
          - code: search:locale_unsupported
            when: The request names a locale this engine has no rules for.
          - code: index:no_primary_key
            when: "The index declares no primary key, so a hit cannot be named."
          - code: search:explain:key_required
            when: "The request carries no `key`, so it names no hit to explain."
          - code: search:explain:index_out_of_range
            when: "`index` is below zero, so it names no value of the `hits` path."
          - code: search:field_unknown
            when: "A clause, sort or facet names a field the index does not have."
          - code: search:usage_unsupported
            when: "A clause, sort or facet uses a field in a way the definition does\
              \ not enable for it."
          - code: search:value_invalid
            when: A matcher is given a value of the wrong kind for the type of its
              field.
          - code: search:matcher:type_unsupported
            when: A matcher is used on a field whose type cannot answer it.
          - code: search:no_searchable_fields
            when: A text clause names no fields and the index has none defined for
              matching.
          - code: search:nested:path_not_nested
            when: A `nested` clause names a path whose values are flattened.
          - code: search:nested:field_not_inside
            when: A clause inside a `nested` clause names a field outside its path.
          - code: search:nested:field_outside
            when: A clause outside a `nested` clause names a field inside a nested
              list.
          - code: search:nested:clause_unsupported
            when: "A `nested` clause holds a clause that cannot run against a single\
              \ value, such as `fuse`."
          - code: search:interpret:unit_required
            when: An `interpret` target names a field that is not a number field or
              declares no `unit`.
          - code: search:interpret:fallback_unit_mismatch
            when: A `fallback` target declares another unit than the target it stands
              in for.
          - code: search:source_not_kept
            when: "`fields` names something only the document copy can answer, and\
              \ the index keeps none."
          - code: search:facet:range_invalid
            when: A range bucket has `to` at or below `from`.
          - code: search:hits:facet_unsupported
            when: A facet of a search with `hits` names a field inside another object
              than the `hits` path.
          - code: search:hits:path_not_nested
            when: The `hits` path names an object field that is not in `nested` mode.
          - code: search:matched:field_not_nested
            when: A `matched` field is an object field that is not in `nested` mode.
          - code: search:sort:nested_unsupported
            when: A sort names a field inside a nested list in a way its values cannot
              be ordered.
          - code: search:cursor:stale
            when: "The cursor was taken under this sort, but the values it carries\
              \ no longer fit how the index defines the sort. Start from the first\
              \ page."
          - code: search:clause:k_required
            when: A `knn` clause carries no `k`.
          - code: search:freshness:invalid
            when: The freshness token is not one the engine issued. Pass a token back
              unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `search` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `search` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `search` permission.
        "404":
          description: |-
            The index does not exist, the key has no permissions on it, or the explanation names something the index does not hold.

            Error codes: `search:explain:document_not_found` - Nothing is indexed under `key`. `search:explain:value_not_found` - The document holds no value along the `hits` path at `index`. `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:explain:document_not_found
            when: Nothing is indexed under `key`.
          - code: search:explain:value_not_found
            when: The document holds no value along the `hits` path at `index`.
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index cannot be searched right now.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The explanation did not finish on the node.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:timeout` - The search behind the explanation ran for longer than `EXOFIND_SEARCH_TIMEOUT`. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:timeout
            when: The search behind the explanation ran for longer than `EXOFIND_SEARCH_TIMEOUT`.
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      x-required-permission: search
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: true
  /v1alpha1/indexes/{name}/suggest:
    post:
      summary: Suggest what to search for
      description: |-
        Answers what to search for, from the text typed into a search box so far. A search box asks for this on every keystroke, and shows the answer as a list to pick from.

        The suggestions are the values of the fields the search settings of the index opt in with `suggest` (see [Field settings](https://exofind.dev/reference/admin-api/#field-settings)), that start with the text, each with how many documents hold it under the given filters. An index whose settings suggest no field answers an empty list. Each suggestion says how many characters of it were typed, so a search box can mark the part that completes the text.

        Matching rules:

        - **Folding**: The text and the values of a field are compared folded in case and Unicode form, by the `normalize` step of the field's `autocomplete` analyzer chain, or of the chain the engine builds for `autocomplete` when the field declares none. `rö` finds `Röd`. Words are not stemmed, so `shoes` does not find `Shoe`.
        - **Declared labels**: The text is also compared with the label the search settings declare for a value in the locale of the request, so `rö` suggests the value `red` labelled `Röd` in Swedish. A declared value no document holds is never suggested.
        - **Whole-value prefix**: The comparison is against the start of the whole value, not of each word: `air` does not find `Nike Air Max`.
        - **Ordering**: The most common values first; ties by field name, then by value.
        - **Typo tolerance**: When fewer values than `limit` start with a text of at least five characters and `typos` is `auto`, values one mistake away from the text - a character inserted, dropped, replaced, or two adjacent ones swapped - are suggested after them, marked `corrected`, with `typed` at `0`. The first character of the text is never read as a mistake.
        - **Counts**: The counts are the ones a facet of a search under the same filters answers. A filter on a suggested field is left out of that field's own counts, so a brand already ticked keeps the other brands suggestable.

        A filter panel that completes the values of one facet uses `POST /v1alpha1/indexes/{name}/facets/{field}/values` instead. See [Suggesting what to search for](https://exofind.dev/reference/search-api/#suggesting-what-to-search-for).

        Requires the `search` permission on the index the path names. The `reader`, `writer` and `admin` roles include it. A node that sets an anonymous key serves this endpoint to requests that carry no credential.
      operationId: suggest
      tags:
      - Search
      parameters:
      - description: "Name of the index. To suggest from one generation, add `@` and\
          \ the name of the generation, such as `books@2`."
        example: books
        name: name
        in: path
        required: true
        schema:
          type: string
      - description: "A freshness token, for a request that carries none in its body.\
          \ The body's `freshness.atLeast` is read when both are given."
        example: AQoIcHJvZHVjdHMSATIYBw
        in: header
        name: X-Exofind-Freshness
      requestBody:
        content:
          application/json:
            examples:
              suggestions:
                summary: "What to search for among shoes, from `adi`"
                value:
                  text: adi
                  filters:
                  - field: category
                    match:
                      value: Shoes
                  limit: 5
            schema:
              $ref: "#/components/schemas/SuggestRequest"
        required: true
      responses:
        "200":
          description: "The suggestions, the most common first."
          content:
            application/json:
              examples:
                suggestions:
                  summary: The answer to the example request
                  value:
                    suggestions:
                    - text: adidas
                      typed: 3
                      field: brand
                      value: adidas
                      count: 87
                    - text: Adidas Originals
                      typed: 3
                      field: brand
                      value: Adidas Originals
                      count: 12
                    generation: "2"
                    tookMs: 0.412
              schema:
                $ref: "#/components/schemas/SuggestResponse"
        "400":
          description: |-
            The request is not one that can be answered, or it asks for more than the node allows. See [Search configuration](https://exofind.dev/reference/configuration/#search) for the caps.

            Error codes: `search:suggest:limit_out_of_range` - `limit` is below 1 or above `EXOFIND_SUGGEST_MAX_LIMIT`. The `max` argument carries the cap. `request:value_required` - A part of the request that needs a value carries none. The `path` names it. `search:clause:field_required` - A `field` clause does not name the field to match. `search:clause:match_required` - A `field` clause does not say what to look for in the field. `search:clause:text_required` - A `text` clause carries no text to search for. `search:clause:path_required` - A `nested` clause does not name the object field to match inside. `search:clause:vector_required` - A `knn` clause carries no vector to find the neighbours of. `search:clause:k_out_of_range` - The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`. `search:clause:weight_out_of_range` - The `weight` of a `boost` clause is missing, below zero or not a finite number. `search:clause:slop_out_of_range` - The `slop` of a `text` clause is below zero. `search:clause:slop_unsupported` - A `text` clause sets `slop` without matching as a phrase. Set `match` to `phrase`, or to `user`. `search:clause:join_unsupported` - A `text` clause sets `join` without matching what somebody typed. Set `match` to `user`, or say `all` or `any` in `match` itself. `search:clause:rankings_too_few` - A `fuse` clause holds fewer than two rankings to fuse. `search:clause:ranking_empty` - A ranking of a `fuse` clause holds nothing to rank by. `search:clause:rank_constant_out_of_range` - The `rankConstant` of a `fuse` clause is not a number above zero. `search:clause:depth_out_of_range` - The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`. `search:clause:interpret_fields_required` - The `interpret` of a `text` clause names no target field. Name at least one, or use `auto` or `off`. `search:clause:interpret_when_unsupported` - The `when` of an `interpret` target holds a `nested`, `knn` or `fuse` clause. `search:matcher:value_required` - A matcher carries no value to look for. `search:matcher:range_empty` - A range matcher carries no bound. `search:matcher:range_conflicting` - A range matcher combines `gte` with `gt`, or `lte` with `lt`. `search:matcher:origin_required` - A distance matcher carries no `lat` and `lon` to measure from. `search:matcher:radius_required` - A distance matcher does not say how far from the origin values may be. `search:filter:clause_unsupported` - A clause under `filters` is neither a `field` nor a `nested` clause. `search:locale_unsupported` - The request names a locale the node has no rules for. `search:field_unknown` - A filter names a field the index does not have. `search:filter:scoring_unsupported` - A clause under `filter` affects the score. Move it out of `filter`. `search:clauses_too_many` - The filter holds more clauses than the node allows. `search:clauses_too_deep` - The filter nests deeper than the node allows. `search:usage_unsupported` - A clause, sort or facet uses a field in a way the definition does not enable for it. `search:value_invalid` - A matcher is given a value of the wrong kind for the type of its field. `search:matcher:type_unsupported` - A matcher is used on a field whose type cannot answer it. `search:no_searchable_fields` - A text clause names no fields and the index has none defined for matching. `search:nested:path_not_nested` - A `nested` clause names a path whose values are flattened. `search:nested:field_not_inside` - A clause inside a `nested` clause names a field outside its path. `search:nested:field_outside` - A clause outside a `nested` clause names a field inside a nested list. `search:nested:clause_unsupported` - A `nested` clause holds a clause that cannot run against a single value, such as `fuse`. `search:interpret:unit_required` - An `interpret` target names a field that is not a number field or declares no `unit`. `search:interpret:fallback_unit_mismatch` - A `fallback` target declares another unit than the target it stands in for. `search:clause:k_required` - A `knn` clause carries no `k`. `search:freshness:invalid` - The freshness token is not one the engine issued. Pass a token back unchanged. `search:freshness:version_unsupported` - The freshness token was issued in a format version this node does not read. The `version` argument carries it; send the request to a node of the release that issued the token. `search:freshness:index_mismatch` - The freshness token is of another index than the one in the path. The `index` argument carries the index the token is of.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: search:suggest:limit_out_of_range
            when: '`limit` is below 1 or above `EXOFIND_SUGGEST_MAX_LIMIT`. The `max`
              argument carries the cap.'
          - code: request:value_required
            when: A part of the request that needs a value carries none. The `path`
              names it.
          - code: search:clause:field_required
            when: A `field` clause does not name the field to match.
          - code: search:clause:match_required
            when: A `field` clause does not say what to look for in the field.
          - code: search:clause:text_required
            when: A `text` clause carries no text to search for.
          - code: search:clause:path_required
            when: A `nested` clause does not name the object field to match inside.
          - code: search:clause:vector_required
            when: A `knn` clause carries no vector to find the neighbours of.
          - code: search:clause:k_out_of_range
            when: "The `k` of a `knn` clause is missing, below one, or above `EXOFIND_SEARCH_MAX_KNN_K`."
          - code: search:clause:weight_out_of_range
            when: "The `weight` of a `boost` clause is missing, below zero or not\
              \ a finite number."
          - code: search:clause:slop_out_of_range
            when: The `slop` of a `text` clause is below zero.
          - code: search:clause:slop_unsupported
            when: "A `text` clause sets `slop` without matching as a phrase. Set `match`\
              \ to `phrase`, or to `user`."
          - code: search:clause:join_unsupported
            when: "A `text` clause sets `join` without matching what somebody typed.\
              \ Set `match` to `user`, or say `all` or `any` in `match` itself."
          - code: search:clause:rankings_too_few
            when: A `fuse` clause holds fewer than two rankings to fuse.
          - code: search:clause:ranking_empty
            when: A ranking of a `fuse` clause holds nothing to rank by.
          - code: search:clause:rank_constant_out_of_range
            when: The `rankConstant` of a `fuse` clause is not a number above zero.
          - code: search:clause:depth_out_of_range
            when: "The `depth` of a `fuse` clause is below one result, or above `EXOFIND_SEARCH_MAX_FUSE_DEPTH`."
          - code: search:clause:interpret_fields_required
            when: "The `interpret` of a `text` clause names no target field. Name\
              \ at least one, or use `auto` or `off`."
          - code: search:clause:interpret_when_unsupported
            when: "The `when` of an `interpret` target holds a `nested`, `knn` or\
              \ `fuse` clause."
          - code: search:matcher:value_required
            when: A matcher carries no value to look for.
          - code: search:matcher:range_empty
            when: A range matcher carries no bound.
          - code: search:matcher:range_conflicting
            when: "A range matcher combines `gte` with `gt`, or `lte` with `lt`."
          - code: search:matcher:origin_required
            when: A distance matcher carries no `lat` and `lon` to measure from.
          - code: search:matcher:radius_required
            when: A distance matcher does not say how far from the origin values may
              be.
          - code: search:filter:clause_unsupported
            when: A clause under `filters` is neither a `field` nor a `nested` clause.
          - code: search:locale_unsupported
            when: The request names a locale the node has no rules for.
          - code: search:field_unknown
            when: A filter names a field the index does not have.
          - code: search:filter:scoring_unsupported
            when: A clause under `filter` affects the score. Move it out of `filter`.
          - code: search:clauses_too_many
            when: The filter holds more clauses than the node allows.
          - code: search:clauses_too_deep
            when: The filter nests deeper than the node allows.
          - code: search:usage_unsupported
            when: "A clause, sort or facet uses a field in a way the definition does\
              \ not enable for it."
          - code: search:value_invalid
            when: A matcher is given a value of the wrong kind for the type of its
              field.
          - code: search:matcher:type_unsupported
            when: A matcher is used on a field whose type cannot answer it.
          - code: search:no_searchable_fields
            when: A text clause names no fields and the index has none defined for
              matching.
          - code: search:nested:path_not_nested
            when: A `nested` clause names a path whose values are flattened.
          - code: search:nested:field_not_inside
            when: A clause inside a `nested` clause names a field outside its path.
          - code: search:nested:field_outside
            when: A clause outside a `nested` clause names a field inside a nested
              list.
          - code: search:nested:clause_unsupported
            when: "A `nested` clause holds a clause that cannot run against a single\
              \ value, such as `fuse`."
          - code: search:interpret:unit_required
            when: An `interpret` target names a field that is not a number field or
              declares no `unit`.
          - code: search:interpret:fallback_unit_mismatch
            when: A `fallback` target declares another unit than the target it stands
              in for.
          - code: search:clause:k_required
            when: A `knn` clause carries no `k`.
          - code: search:freshness:invalid
            when: The freshness token is not one the engine issued. Pass a token back
              unchanged.
          - code: search:freshness:version_unsupported
            when: The freshness token was issued in a format version this node does
              not read. The `version` argument carries it; send the request to a node
              of the release that issued the token.
          - code: search:freshness:index_mismatch
            when: The freshness token is of another index than the one in the path.
              The `index` argument carries the index the token is of.
        "401":
          description: |-
            The request carries no credential this node accepts. Absent, malformed, unknown and lapsed keys are all answered alike, so a refusal cannot be used to find out which keys exist. The response carries `WWW-Authenticate: Bearer`.

            Error codes: `auth:unauthenticated` - The request carries no credential this node accepts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:unauthenticated
            when: The request carries no credential this node accepts.
        "403":
          description: |-
            The API key does not have the `search` permission.

            Error codes: `auth:forbidden` - The key is accepted but does not hold the `search` permission.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: auth:forbidden
            when: The key is accepted but does not hold the `search` permission.
        "404":
          description: |-
            The index does not exist, or the key has no permissions on it. An index on which a key has no permissions returns this status as though it did not exist.

            Error codes: `index:not_found` - The node holds no such index, or the key has no permission on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:not_found
            when: "The node holds no such index, or the key has no permission on it."
        "409":
          description: |-
            The index currently has no live generation (`index:no_live_generation`). Promote a generation and send the request again.

            Error codes: `index:no_live_generation` - The index has no live generation. Promote one and send the request again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:no_live_generation
            when: The index has no live generation. Promote one and send the request
              again.
        "503":
          description: |-
            The request raced the index being closed to free local resources (`index:closed`). Sending the same request again reopens it.

            Also returned when counting collected for longer than `EXOFIND_SUGGEST_TIMEOUT` (`search:timeout`). The counts collected before the node stopped are dropped, so narrow the filters instead of repeating the request.

            Error codes: `index:closed` - The request raced the index being closed to free local resources. Sending it again reopens the index. `search:timeout` - Collecting ran for longer than `EXOFIND_SUGGEST_TIMEOUT`. What it collected is dropped, so narrow the filters rather than sending the request again. `search:freshness:unavailable` - The node did not reach the state the freshness token asks for within `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
          x-error-codes:
          - code: index:closed
            when: The request raced the index being closed to free local resources.
              Sending it again reopens the index.
          - code: search:timeout
            when: "Collecting ran for longer than `EXOFIND_SUGGEST_TIMEOUT`. What\
              \ it collected is dropped, so narrow the filters rather than sending\
              \ the request again."
          - code: search:freshness:unavailable
            when: The node did not reach the state the freshness token asks for within
              `EXOFIND_SEARCH_FRESHNESS_WAIT`. Send the request again after the `Retry-After`
              header.
      security:
      - apiKey: []
      x-required-permission: search
      x-permission-scope: index
      x-permission-roles:
      - reader
      - writer
      - admin
      x-permission-anonymous: true
info:
  title: Exofind
  version: v1alpha1
  description: Search indexes kept in S3-compatible object storage. Every endpoint
    accepts and returns JSON.
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
