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

# Suggest the relations your documents already hold

> The attributes that look like relations, found in a sample of the
namespace's documents before you declare any. It creates nothing.

First, over a sample of up to 1,000 documents, the string attributes
with few distinct values (at most 20, some shared by several
documents) are candidate kinds. One is taken as the kind and named in
`kind.attribute` with its values; the others are listed in
`kind.candidates`, since a status or a plan has as few values as a
kind. Pass `kind` to take another candidate. Then, for up to 100
sampled documents of each kind, every string attribute whose values
name live documents is listed in `relations`: the kind it is on
(`from`), the kind its values name (`to`), and how many of its
present values resolved. Each element of an array counts as one
value, with the arrays' lengths beside them. A value that names the
document holding it is a self-reference, counted apart.

A value that resolves to a document id is evidence of a possible
relation, not proof of one: check it before you declare it on a
judgment. Counts are of the sample, not of the namespace.

The work is bounded: documents read, attributes examined, ids looked
up and bytes read share one budget, reported in `budget`. When a
dimension runs out, the response holds what was found so far, with
`complete` false and `exhausted` naming the dimension. The same
namespace state gives the same sample, so asking again without
writing gives the same suggestions.

On a template prefix, the sample is one tenant's, named in `tenant`,
as an example; ask on another tenant's namespace to see its own.

Billed like a query, as `bytes_scanned` in `usage`: the bytes of the
documents it reads, the same as `budget.bytes_read.used`, which the
fixed budget bounds.
On every plan.




## OpenAPI

````yaml /api-reference/openapi.yaml get /namespaces/{ns}/relations/suggest
openapi: 3.1.0
info:
  title: Brussle API
  version: '1'
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  description: |
    The `/v1` contract. It is locked: changes within `/v1`
    are additive only, and anything breaking is `/v2`.

    - **Base URL.** `https://api.brussle.com/v1`.
    - **Evolution.** Response objects may gain fields and enums may gain values.
      Clients must ignore what they do not know. Request objects reject unknown
      fields.
    - **Path segments.** Namespace names (`{ns}`) and document ids (`{id}`) may
      contain `/`. Send it percent-encoded as `%2F`, so that
      `acme/prod/tenant_123` is `acme%2Fprod%2Ftenant_123`.
    - **Templates.** The judgment routes also take a namespace prefix
      ending in `/*`, such as `acme%2Fprod%2F*`. A judgment created there is a
      template: every namespace under the prefix inherits it, including ones
      created later. The most specific matching prefix wins.
    - **Thresholds are settings.** Named thresholds belong to the
      judgment, not to a version. They are evaluated at read time against the
      raw fields, so a change applies at once to every answer, with no
      recompute and no new version.
    - **Entity judgments.** A judgment can read the documents
      that point at the judged one, through `context.related`, and apply only
      to documents matching `applies_to`. Its answers carry a
      `watermark`, and creating one that runs `on_change` returns a replay
      estimate of its monthly cost until you confirm.
    - **Referenced documents.** A relation can also read the
      one document the judged document points at, through
      `join: {theirs: "id", mine: "attributes.<name>"}`. A change to what the
      relation renders of that document re-judges the judged documents that
      point at it and are inside `freshness.fanout.scope`: a fan-out.
    - **Relations.** A relation can read other judgments' answers (banded
      or cut, never summed), up to three judgments deep and never in a
      cycle, or be a blocking relation over the documents that
      share a key, which a `choice` can choose among with `options.from`. A
      recipe can read the document as the judgment last saw it
      (`context.previous`). A judgment that can fan out confirms its rolling
      limits once, at create; past them, work is deferred, reads `stale`
      with an event, and catches up on its own. Nothing waits for a confirm
      at runtime. Groups keep aggregates per key value for
      reporting.
    - **Idempotency.** Every write operation is idempotent by construction:
      a retried append adds nothing while its values are still in the array,
      and a retried upsert or patch sets the same content again.
      Every request that changes something also takes an `Idempotency-Key`,
      so that creates, activations, backfills, draws and the rest can be
      retried safely too: a retry with the key gets the first response back,
      verbatim and marked `Idempotent-Replayed: true`, instead of running
      again (see the parameter).
    - **Errors.** Every error is the `Error` envelope; the HTTP status follows
      the code (see each response).
    - **Rate limits.** Every response to a request whose key was accepted
      carries `X-RateLimit-Limit` and `X-RateLimit-Remaining`, which count
      the key's requests a second; a request refused before its key is
      checked (`unauthorized`, or a route that does not exist) carries
      neither. A `rate_limited` response adds `Retry-After`, except a daily
      allowance that is used up, which says when it renews in
      `details.resets_at` instead. Every `503` carries `Retry-After` too.
      Reads of one namespace also have a concurrency limit: past 32 gets and
      queries in flight at once, one more is `rate_limited` with
      `Retry-After` and `details.concurrent_reads`, whatever the rate-limit
      headers show.
    - **Usage.** Every response that bills carries `usage`. Judging is billed
      per judgment, engine-neutral, by size class: one judgment answered counts
      by the tokens of its compiled context and its question together, 1 if
      standard (up to 2,000 tokens), 4 if large (up to 8,000) and 16 if
      extra-large (up to 32,000, the engine maximum). No response ever
      carries an engine's price.
servers:
  - url: https://api.brussle.com/v1
security:
  - apiKey: []
tags:
  - name: Namespaces
    description: Namespaces, their settings and cache warming.
  - name: Documents
    description: Writes, point reads and queries.
  - name: Judgments
    description: >-
      Judgment definitions, versions, settings, activation, backfill and
      template detach.
  - name: Outcomes and calibration
    description: |
      Post what actually happened to judged documents, and read how well answers
      match it: the calibration report, calibrated answers and threshold
      recommendations. See [calibration](/concepts/calibration).
  - name: Groups
    description: >
      Groups keep aggregates per value of a key attribute, a group-by you
      declare for reporting; a template's tenant

      summary publishes each tenant's groups, banded, as a document the

      platform can judge.
  - name: Jobs
    description: >-
      Backfill, shadow, periodic, deletion, reference index, evaluation export,
      resync, group build, discovery and simulation jobs.
  - name: Engines
    description: The engine registry.
  - name: Organization
    description: |
      Settings of the whole organization: prefixes marked non-production,
      which the tenant fee and template calibration pools leave out.
  - name: Subscriptions
    description: |
      Saved queries that send an event when a document starts or stops
      matching: the query's filter grammar, on a namespace or a
      template prefix, with the fields each event carries.
  - name: Webhooks
    description: |
      Webhook endpoints, the events feed, the delivery log, redelivery and
      recovery. Every event type and its body is under `webhooks`. Events
      are delivered at least once, signed as Standard Webhooks, and kept
      for 30 days. Webhooks are on every plan, and deliveries are not
      billed; plans set how many endpoints an organization has.
paths:
  /namespaces/{ns}/relations/suggest:
    parameters:
      - $ref: '#/components/parameters/NamespaceOrTemplate'
    get:
      tags:
        - Judgments
      summary: Suggest the relations your documents already hold
      description: |
        The attributes that look like relations, found in a sample of the
        namespace's documents before you declare any. It creates nothing.

        First, over a sample of up to 1,000 documents, the string attributes
        with few distinct values (at most 20, some shared by several
        documents) are candidate kinds. One is taken as the kind and named in
        `kind.attribute` with its values; the others are listed in
        `kind.candidates`, since a status or a plan has as few values as a
        kind. Pass `kind` to take another candidate. Then, for up to 100
        sampled documents of each kind, every string attribute whose values
        name live documents is listed in `relations`: the kind it is on
        (`from`), the kind its values name (`to`), and how many of its
        present values resolved. Each element of an array counts as one
        value, with the arrays' lengths beside them. A value that names the
        document holding it is a self-reference, counted apart.

        A value that resolves to a document id is evidence of a possible
        relation, not proof of one: check it before you declare it on a
        judgment. Counts are of the sample, not of the namespace.

        The work is bounded: documents read, attributes examined, ids looked
        up and bytes read share one budget, reported in `budget`. When a
        dimension runs out, the response holds what was found so far, with
        `complete` false and `exhausted` naming the dimension. The same
        namespace state gives the same sample, so asking again without
        writing gives the same suggestions.

        On a template prefix, the sample is one tenant's, named in `tenant`,
        as an example; ask on another tenant's namespace to see its own.

        Billed like a query, as `bytes_scanned` in `usage`: the bytes of the
        documents it reads, the same as `budget.bytes_read.used`, which the
        fixed budget bounds.
        On every plan.
      operationId: suggestRelations
      parameters:
        - name: kind
          in: query
          required: false
          description: >-
            The attribute to take as the kind, one of the candidates. Omitted,
            the response picks one and names it.
          schema:
            type: string
            minLength: 1
      responses:
        '200':
          description: >-
            The suggestions, from a bounded sample; `complete` says whether the
            budget ran out.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RelationSuggestions'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Internal'
components:
  parameters:
    NamespaceOrTemplate:
      name: ns
      in: path
      required: true
      description: |
        A namespace name, or a template prefix ending in `/*`, with any
        `/` sent as `%2F`: `acme%2Fprod%2Ftenant_123` or `acme%2Fprod%2F*`.
      schema:
        $ref: '#/components/schemas/NamespaceOrTemplate'
  headers:
    RateLimitLimit:
      description: >-
        Requests allowed per second for this key. Only on responses to a request
        whose key was accepted.
      schema:
        type: integer
        minimum: 0
    RateLimitRemaining:
      description: >-
        Requests left in the current one-second window for this key. It does not
        count the per-namespace limit on reads in flight.
      schema:
        type: integer
        minimum: 0
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
  schemas:
    RelationSuggestions:
      type: object
      description: |
        The attributes that look like relations, from a bounded sample of
        documents. A value that resolves to a document id is evidence of a
        possible relation, not proof of one; nothing is created.
      required:
        - tenant
        - sampled_documents
        - kind
        - relations
        - complete
        - exhausted
        - budget
        - note
        - usage
      properties:
        tenant:
          type:
            - string
            - 'null'
          description: >-
            On a template prefix, the tenant namespace whose documents were
            sampled, as an example. Null on a namespace.
        sampled_documents:
          type: integer
          minimum: 0
          description: Documents in the sample.
        kind:
          $ref: '#/components/schemas/SuggestedKinds'
        relations:
          type: array
          description: >-
            Each attribute of each kind whose values resolve to documents, kinds
            with the most sampled documents first, attributes in name order.
          items:
            $ref: '#/components/schemas/SuggestedRelation'
        complete:
          type: boolean
          description: >-
            False when a budget dimension ran out before every kind and
            attribute was examined; what was found is still returned.
        exhausted:
          description: The budget dimension that ran out. Null when `complete`.
          oneOf:
            - type: string
              enum:
                - bytes_read
                - attributes_examined
                - ids_looked_up
            - type: 'null'
        budget:
          $ref: '#/components/schemas/SuggestionBudget'
        note:
          type: string
          description: >-
            Says that a resolving value is evidence of a possible relation, not
            proof, and that nothing was created.
        usage:
          $ref: '#/components/schemas/Usage'
    NamespaceOrTemplate:
      type: string
      description: |
        A namespace name, or a template prefix: a namespace path ending in
        `/*`, such as `acme/prod/*`, which every namespace under `acme/prod/`
        inherits judgments from. Up to 256 bytes. Never exactly
        `.` or `..`, which a URL path can't carry.
      pattern: ^(?!\.\.?$)[A-Za-z0-9._:/-]+(/\*)?$
      minLength: 1
      maxLength: 256
    SuggestedKinds:
      type: object
      description: How the sample was split into kinds.
      required:
        - attribute
        - values
        - candidates
      properties:
        attribute:
          type:
            - string
            - 'null'
          description: >-
            The attribute taken as the kind. Null when no attribute qualifies,
            and the sample is treated as one kind.
        values:
          type: array
          description: >-
            Its values in the sample, most documents first; `value` null counts
            the documents without it.
          items:
            $ref: '#/components/schemas/KindCount'
        candidates:
          type: array
          description: >-
            The other attributes that could be the kind, each with its values.
            Pass one as `kind` to take it instead.
          items:
            type: object
            required:
              - attribute
              - values
            properties:
              attribute:
                type: string
              values:
                type: array
                items:
                  $ref: '#/components/schemas/KindCount'
    SuggestedRelation:
      type: object
      description: |
        One attribute, on the sampled documents of one kind, whose values
        resolve to documents. Every count is of present values, each array
        element counting once: `resolved`, `self_references` and
        `unresolved` add up to `sampled_values`.
      required:
        - attribute
        - from
        - to
        - resolved
        - sampled_values
        - self_references
        - unresolved
        - unresolved_examples
        - resolved_by_kind
        - documents
        - array_lengths
      properties:
        attribute:
          type: string
        from:
          type:
            - string
            - 'null'
          description: >-
            The kind of the documents holding the attribute; null for documents
            without the kind attribute.
        to:
          type:
            - string
            - 'null'
          description: >-
            The kind most of its resolved values name. Null when only
            self-references resolved, or the documents named have no kind.
        resolved:
          type: integer
          minimum: 0
          description: Values naming another live document.
        sampled_values:
          type: integer
          minimum: 0
          description: Present values examined, the denominator.
        self_references:
          type: integer
          minimum: 0
          description: >-
            Values naming the document that holds them. An attribute whose
            values are all self-references holds the document's own id, not a
            relation.
        unresolved:
          type: integer
          minimum: 0
          description: Values naming no live document.
        unresolved_examples:
          type: array
          maxItems: 5
          description: Up to 5 distinct values that named no live document.
          items:
            type: string
        resolved_by_kind:
          type: array
          description: >-
            The resolved values by the kind of the document they name, most
            first.
          items:
            type: object
            required:
              - kind
              - resolved
            properties:
              kind:
                type:
                  - string
                  - 'null'
              resolved:
                type: integer
                minimum: 0
        documents:
          type: integer
          minimum: 0
          description: Sampled documents of `from` that hold the attribute, at most 100.
        array_lengths:
          description: >-
            The lengths of its arrays, over the documents holding one. Null when
            no value is an array.
          oneOf:
            - type: object
              required:
                - documents
                - min
                - max
                - mean
              properties:
                documents:
                  type: integer
                  minimum: 1
                min:
                  type: integer
                  minimum: 0
                max:
                  type: integer
                  minimum: 0
                mean:
                  type: number
                  minimum: 0
            - type: 'null'
    SuggestionBudget:
      type: object
      description: What the suggestion spent of each budget dimension.
      required:
        - documents_read
        - attributes_examined
        - ids_looked_up
        - bytes_read
      properties:
        documents_read:
          $ref: '#/components/schemas/BudgetSpent'
        attributes_examined:
          $ref: '#/components/schemas/BudgetSpent'
        ids_looked_up:
          $ref: '#/components/schemas/BudgetSpent'
        bytes_read:
          $ref: '#/components/schemas/BudgetSpent'
    Usage:
      type: object
      description: What this response billed.
      minProperties: 1
      properties:
        bytes_written:
          type: integer
          format: int64
          minimum: 0
          description: |
            The data the write stores, as compact JSON in UTF-8: its operations'
            ids and fields, or the outcomes posted. Whitespace and `\u` escapes
            of non-ASCII text in the request are not counted, nor are
            `wait_for` and `wait_timeout_ms`, so the same data counts the same
            however it was encoded.
        bytes_scanned:
          type: integer
          format: int64
          minimum: 0
        judgment_units:
          type: integer
          format: int64
          minimum: 0
          description: >
            Judgments billed, each counted by its size class: the tokens

            of its compiled context and its question together (the question's

            text, criteria and every option or level) make it standard (up to

            2,000 tokens, counts 1), large (up to 8,000, counts 4) or

            extra-large (up to 32,000, counts 16). They are metered when the
            answer is recorded, so writes with `wait_for` and reads with
            `wait_ms`

            return the answers without their count; it appears in your usage.
        tokens_evaluated:
          type: integer
          format: int64
          minimum: 0
          deprecated: true
          description: Never reported. Judging is billed in `judgment_units`.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
            details:
              type: object
              description: >-
                Code-specific detail, such as the scan estimate on a refused
                query. Every `internal` error has `request_id`.
              properties:
                reason:
                  type: string
                  description: >
                    Why, where one code has more than one cause. On `forbidden`,

                    `payment_overdue`: a payment has been unpaid for 14 days, so
                    the

                    organization's keys are read-only until it is paid; reads
                    still

                    work, and paying restores writes within a few minutes. A key

                    that is read-only itself is `forbidden` without a `reason`.
                    On

                    the events feed's `invalid_request`, `cursor_expired`.
                suggested_context:
                  description: >-
                    On a create without a context recipe, the recipe suggested
                    from the namespace's documents, or null when there are none.
                  oneOf:
                    - $ref: '#/components/schemas/SuggestedContext'
                    - type: 'null'
    KindCount:
      type: object
      required:
        - value
        - documents
      properties:
        value:
          type:
            - string
            - 'null'
        documents:
          type: integer
          minimum: 0
    BudgetSpent:
      type: object
      required:
        - used
        - limit
      properties:
        used:
          type: integer
          minimum: 0
        limit:
          type: integer
          minimum: 0
    ErrorCode:
      type: string
      description: |
        HTTP status by code: `invalid_request` 400, `unauthorized` 401,
        `budget_exceeded` 402, `plan_required` 402, `forbidden` 403,
        `not_found` 404, `conflict` 409,
        `too_large` 413, `engine_version_unavailable` 422,
        `insufficient_labels` 422, `idempotency_key_reused` 422,
        `rate_limited` 429, `internal` 500,
        `engine_unavailable` 503, `unavailable` 503.
      enum:
        - invalid_request
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - too_large
        - rate_limited
        - budget_exceeded
        - plan_required
        - engine_unavailable
        - engine_version_unavailable
        - unavailable
        - insufficient_labels
        - idempotency_key_reused
        - internal
    SuggestedContext:
      type: object
      description: |
        The recipe suggested for a create without `context`, in
        the refusal's `details.suggested_context`. From a sample of the
        documents the definition applies to: it keeps short text, numbers,
        booleans and small attributes, and leaves out ids, timestamps, links,
        binary-looking and very long values, and rarely present fields;
        `excluded` says why for each. `max_tokens` is set to keep the context
        in the standard size class, at most 2,000. The same documents always
        give the same recipe.
      required:
        - recipe
        - cost_per_answer
        - whole_state_cost_per_answer
        - sampled_documents
        - excluded
      properties:
        recipe:
          $ref: '#/components/schemas/ContextRecipe'
        cost_per_answer:
          type:
            - number
            - 'null'
          minimum: 0
          description: >-
            Mean judgments per answer, counted by size class, over the sample
            with this recipe and the definition's question. The suggested
            `max_tokens` is at most 2,000, the standard ceiling, less the
            question's tokens, so a context that fills it stays standard with
            its question.
        whole_state_cost_per_answer:
          type:
            - number
            - 'null'
          minimum: 0
          description: The same with the whole `state`.
        sampled_documents:
          type: integer
          minimum: 1
        excluded:
          type: array
          description: >-
            Each path left out, and why, so you can put back what the question
            needs.
          items:
            type: object
            required:
              - path
              - reason
            properties:
              path:
                type: string
              reason:
                type: string
                enum:
                  - id
                  - timestamp
                  - url
                  - binary
                  - long
                  - rare
                  - too_many
    ContextRecipe:
      type: object
      description: |
        What the engine sees. A create without it is refused with a suggested
        recipe, unless `use_context` asks for the suggestion or the whole
        `state`; a version with no recipe sends the whole
        `state`, subject to the engine limit.
      additionalProperties: false
      properties:
        fields:
          type: array
          description: >-
            Paths included in order, each verbatim or, as a `ClippedPath`, cut
            to its first `max_chars` characters.
          items:
            $ref: '#/components/schemas/RecipeField'
        last_n:
          type: object
          description: Per array path, keep the last n elements.
          additionalProperties:
            type: integer
            minimum: 1
        window:
          type: object
          description: >-
            Per array path whose elements have a timestamp field `at`, keep
            elements within the window.
          additionalProperties:
            $ref: '#/components/schemas/Duration'
        max_tokens:
          type: integer
          minimum: 1
          description: Hard cap; the compiler truncates from the end of the last field.
        related:
          type: object
          description: >
            Up to 4 relations, by name: documents in the same namespace that
            point at the judged one, or the one document it points at. Each
            renders as a `related.<name>`

            entry after `fields`, and a write that changes what it renders

            makes the judged document's answer `pending` until it is judged

            again (for a referenced document, only inside the re-judge scope;

            outside it, the answer is `stale` with `stale_reason`

            `referenced_changed`).
          minProperties: 1
          maxProperties: 4
          propertyNames:
            $ref: '#/components/schemas/Name'
          additionalProperties:
            $ref: '#/components/schemas/Relation'
        previous:
          $ref: '#/components/schemas/PreviousRecipe'
    RecipeField:
      description: >-
        A `state` or `attributes` path of the judged document, rendered
        verbatim, or a `ClippedPath`.
      oneOf:
        - type: string
          pattern: ^(state|attributes)(\.[^.]+)+$
        - $ref: '#/components/schemas/ClippedPath'
    Duration:
      type: string
      description: A whole number and a unit (`s`, `m`, `h` or `d`), such as `7d` or `1h`.
      pattern: ^[1-9][0-9]*[smhd]$
    Relation:
      type: object
      description: >
        Documents that point at the judged document.

        They are ordered newest `created_at` first unless `order` says

        otherwise: `window` keeps those created within the window, the order

        is applied, then `last_n` keeps the first n. At least one of `last_n`

        and `window` is required, and at least one of `fields` and

        `aggregate`. A relation reads at most the newest 1,000 documents,

        and an `order` sorts within them.


        With `join: {theirs: "id", mine: "attributes.<name>"}`

        the relation reads the one document the judged document points at,

        its referenced document, and takes no `window`. With `last_n` (up to

        254) it is a named list instead: the attribute holds an array of

        ids, and the relation reads the documents it names in list order

        unless `order` says otherwise, each once, at most `last_n` of them. An
        id that names nothing, or a

        document outside `match`, renders nothing and is listed in the

        evaluation's `related_documents` as `unresolved`. An attribute a

        named list reads holds at most 254 distinct ids.


        With `join: {theirs: "attributes.<key>", mine:

        "attributes.<key>"}` it is a blocking relation over the documents

        that share the judged document's key value. It needs `window`, takes

        `last_n` up to 254 (up to the `options.from` candidate limit when a

        `choice` chooses among it with

        `options.from`), and a change to its block re-judges the block's

        judged documents. `fields`, `aggregate` and `match` may read other

        judgments' answers (`answers.<j>.<field>`), banded or cut, and the

        newest outcome posted for each related document

        (`outcomes.<j>.value`), shown as posted: the one with the latest

        `observed_at`, of two at the same time the one posted last, about

        the document's current life and of the judgment's current answer

        type. Posting one re-judges the judgments that read it, within their

        `rolling_limit`. A relation reads at most 4 judgments' answers or

        outcomes.
      additionalProperties: false
      required:
        - match
        - join
      allOf:
        - description: A relation renders fields or aggregates.
          if:
            not:
              required:
                - fields
          then:
            required:
              - aggregate
        - description: >-
            A relation over the documents that point at the judged one is
            bounded by `last_n` or `window`.
          if:
            properties:
              join:
                type: object
                properties:
                  mine:
                    const: id
          then:
            anyOf:
              - required:
                  - last_n
              - required:
                  - window
        - description: A relation that reads the referenced document takes no `window`.
          if:
            properties:
              join:
                type: object
                properties:
                  theirs:
                    const: id
          then:
            not:
              required:
                - window
        - description: >-
            A relation that reads the referenced document takes `last_n` up to
            254, as a named list.
          if:
            properties:
              join:
                type: object
                properties:
                  theirs:
                    const: id
          then:
            properties:
              last_n:
                type: integer
                maximum: 254
        - description: A blocking relation needs `window`, and reads at most 254 documents.
          if:
            properties:
              join:
                $ref: '#/components/schemas/BlockingJoin'
          then:
            required:
              - window
            properties:
              last_n:
                type: integer
                maximum: 254
        - description: >-
            Only a relation over the documents that point at the judged one, or
            a named list, takes `order`.
          if:
            required:
              - order
            properties:
              order:
                $ref: '#/components/schemas/RelationOrder'
          then:
            anyOf:
              - properties:
                  join:
                    $ref: '#/components/schemas/ReferringJoin'
              - required:
                  - last_n
                properties:
                  join:
                    $ref: '#/components/schemas/ReferencedJoin'
      properties:
        match:
          $ref: '#/components/schemas/RelationMatch'
          description: Which documents the relation reads.
        join:
          $ref: '#/components/schemas/RelationJoin'
        last_n:
          type: integer
          minimum: 1
          maximum: 1000
          description: >-
            Keep the first n in the relation's order, newest first by default.
            On a relation that reads a referenced document it makes a named
            list, keeping the first n documents the list names, in list order
            unless `order` says otherwise, at most 254; at most 254 on a
            blocking relation, and at most the `options.from` candidate limit
            (see Limits) when `options.from` chooses among it.
        window:
          $ref: '#/components/schemas/Duration'
          description: >-
            Keep documents created within this long before the later of the
            judged document's newest write and its newest related creation, at
            the answer's watermark. An edit to an old related document never
            moves the window. Not on a relation that reads a referenced
            document.
        fields:
          type: array
          description: >-
            Paths to include from each document, rendered under `records` in the
            relation's order. An entry may be a `BandedField`, which renders a
            number as its band, or a `ClippedPath`, which renders the first
            `max_chars` characters. An entry may also be a judgment's
            `answers.<j>.value` and `answers.<j>.thresholds.<name>` as they are,
            and its `.p`, `.score` and `.dist.<option>` only banded.
          minItems: 1
          items:
            oneOf:
              - $ref: '#/components/schemas/RelationField'
              - $ref: '#/components/schemas/BandedField'
              - $ref: '#/components/schemas/ClippedPath'
        aggregate:
          $ref: '#/components/schemas/Aggregate'
        order:
          $ref: '#/components/schemas/RelationOrder'
    PreviousRecipe:
      type: object
      description: >
        The judged document as the judgment's last successful evaluation saw it,
        rendered as `previous.<path>` entries

        after `fields`, so a question can ask what changed. When a revision

        renders the same as the stored current, as a nightly re-upsert of

        the same record does, the context is the one the last verdict saw

        and costs nothing. A document with no stored rendering in its

        incarnation renders nothing under `previous`, and its evaluation

        says `first_revision: true`. Truncation cuts `previous` before

        `fields`.
      additionalProperties: false
      required:
        - fields
      properties:
        fields:
          type: array
          description: >-
            The document's own paths to render as they were, in order, each
            verbatim or cut to `max_chars`.
          minItems: 1
          items:
            $ref: '#/components/schemas/RecipeField'
        anchor:
          type: string
          description: >-
            An attribute such as `attributes.approved_at`. The stored previous
            moves on only when this attribute's value changes, so every edit is
            compared with the revision last approved, however many rejected
            edits come between.
          pattern: ^attributes\.[A-Za-z0-9_-]+$
    ClippedPath:
      type: object
      description: |
        A path rendered as its first `max_chars` characters (Unicode code
        points, not bytes or tokens). A string longer than that is cut to
        it; a value that fits renders unchanged and keeps its type; any
        other longer value renders as its JSON text cut to `max_chars`, a
        string. An edit past the cut changes nothing the engine reads from
        the field. It bounds one field; it does not make a context fit.
      additionalProperties: false
      required:
        - path
        - max_chars
      properties:
        path:
          type: string
          description: >-
            In a recipe's or `previous`'s `fields`, a `state` or `attributes`
            path; in a relation's `fields`, any path a `RelationField` names.
          pattern: >-
            ^((state|attributes)(\.[^.]+)+|id|created_at|updated_at|answers\.[A-Za-z0-9_-]+\.(value|thresholds\.[A-Za-z0-9_-]+))$
        max_chars:
          type: integer
          minimum: 1
          maximum: 4294967295
    RelationMatch:
      type: object
      description: >
        Which documents a relation reads: an `AttributeFilter`, and also a
        judgment's answer: `answers.<j>.value` or

        `answers.<j>.thresholds.<name>` compared by equality (a list is

        `In`), or `answers.<j>.p` or `.score` with an inline cut such as

        `{"gte": 0.8}`; or a document's newest outcome,

        `outcomes.<j>.value`, by equality, or `{"exists": true}` for any

        document that has one. Keys combine with `And`. Membership by

        answer changes only when a document crosses the cut, and the

        relation reads at most 4 × `last_n` candidates to find them, never

        more than the newest 1,000, rendering `capped: true` when that runs

        out first.
      minProperties: 1
      maxProperties: 8
      propertyNames:
        type: string
        pattern: >-
          ^(attributes\.[A-Za-z0-9_-]+|answers\.[A-Za-z0-9_-]+\.(value|p|score|thresholds\.[A-Za-z0-9_-]+)|outcomes\.[A-Za-z0-9_-]+\.value)$
      additionalProperties:
        oneOf:
          - $ref: '#/components/schemas/AttributeFilterValue'
          - type: array
            minItems: 1
            items:
              $ref: '#/components/schemas/AttributeFilterValue'
          - $ref: '#/components/schemas/AnswerCut'
          - $ref: '#/components/schemas/OutcomeExists'
    RelationJoin:
      description: >
        How a related document is matched to the judged one: one side is

        `id` and the other an attribute holding an id, or both sides the same
        attribute, a shared key, or one side

        `id` and the other a matching judgment's pick (a judged link). Two
        different

        attributes are `invalid_request`.
      oneOf:
        - $ref: '#/components/schemas/ReferringJoin'
        - $ref: '#/components/schemas/ReferencedJoin'
        - $ref: '#/components/schemas/BlockingJoin'
        - $ref: '#/components/schemas/LinkedJoin'
    RelationField:
      type: string
      description: >-
        A path in a related document, `id`, `created_at` or `updated_at`; a
        judgment's `answers.<j>.value` or `answers.<j>.thresholds.<name>`, which
        render as they are; or the document's newest outcome,
        `outcomes.<j>.value`, rendered as posted. An answer's `p`, `score` or
        `dist.<option>` must be a `BandedField`.
      pattern: >-
        ^((state|attributes)(\.[^.]+)+|id|created_at|updated_at|answers\.[A-Za-z0-9_-]+\.(value|thresholds\.[A-Za-z0-9_-]+)|outcomes\.[A-Za-z0-9_-]+\.value)$
    BandedField:
      type: object
      description: >
        A number rendered as its band rather than its value, in any relation. In
        a relation's `fields`, `max_chars` cuts the rendered label as a
        `ClippedPath` does; it is refused on an aggregate's `min` or `max`. A
        number renders as the label whose position

        is how many cut points are at or below it: with `bands: [0.3, 0.7]`,

        0.29 is `labels[0]`, 0.3 is `labels[1]` and 0.7 or more `labels[2]`.

        A value that is not a number renders unchanged, and a missing one is

        left out. Only a move to another band changes the context, so a move

        inside one neither fans out nor costs an evaluation. `bands` must be

        strictly increasing and `labels` must have exactly one more entry;

        the server refuses anything else with `invalid_request`.
      additionalProperties: false
      required:
        - path
        - bands
        - labels
      properties:
        path:
          type: string
          description: >-
            A `state` or `attributes` path, or a judgment's `answers.<j>.p`,
            `answers.<j>.score` or `answers.<j>.dist.<option>`.
          pattern: >-
            ^((state|attributes)(\.[^.]+)+|answers\.[A-Za-z0-9_-]+\.(p|score|dist\.[^.]+))$
        bands:
          type: array
          description: Cut points, strictly increasing.
          minItems: 1
          maxItems: 9
          items:
            type: number
        labels:
          type: array
          description: One label per band, lowest first, so one more than `bands`.
          minItems: 2
          maxItems: 10
          items:
            type: string
            minLength: 1
            maxLength: 64
        max_chars:
          type: integer
          minimum: 1
          maximum: 4294967295
          description: >-
            The label cut to this many characters, after the band. Only in a
            relation's `fields`.
    Aggregate:
      type: object
      description: |
        Numbers over the relation's documents, after `match`, `window` and
        `last_n`, rendered as keys of its `related.<name>` entry, such as
        `count` and `sum(state.amount)`. At most 8 paths in total, answer
        paths included. `sum`, `avg`, `min` and `max` skip values that are
        not numbers; `sum` of nothing is 0, and `avg`, `min` and `max` of
        nothing are null. `latest` is the value in the newest document that
        has one, of any JSON type.

        `latest` reads the most recently created document whatever the
        relation's `order`.

        `count_where` counts documents by judgments'
        answers; a `min` or `max` entry may be banded, and one over an
        answer's `p` or `score` must be, rendering the band of the extreme;
        `latest` may read an answer's `value` or `thresholds.<name>`. `sum`
        and `avg` may read an answer's `p`, `score` or `dist.<option>` only
        as a feature the judgment names: never rendered, since it moves on
        every child evaluation. A child whose newest answer failed adds its
        last good value; a child with no answer adds nothing and is outside
        the average. `count_where` and `latest` may read an outcome;
        `labelled` counts the documents that have one.

        `render: false` keeps the aggregates, all but `latest`, out of the
        context the engine reads, while features still read them: a change
        that moves only them then re-judges with no engine call and no
        charge.
      additionalProperties: false
      minProperties: 1
      properties:
        count:
          const: true
          description: How many documents the relation selected.
        labelled:
          const: true
          description: >-
            How many selected documents have an outcome the relation reads
            (`outcomes.<j>.value`). Only on a relation that reads one.
        count_where:
          $ref: '#/components/schemas/CountWhere'
        sum:
          $ref: '#/components/schemas/SummedPaths'
        avg:
          $ref: '#/components/schemas/SummedPaths'
          description: Paths to average, the mean of the numbers there.
        min:
          $ref: '#/components/schemas/ExtremePaths'
        max:
          $ref: '#/components/schemas/ExtremePaths'
        latest:
          $ref: '#/components/schemas/LatestPaths'
        render:
          type: boolean
          default: true
          description: >-
            Whether the aggregates, all but `latest`, are in the context the
            engine reads. Part of the version.
    RelationOrder:
      type: object
      description: |
        Which of a relation's documents come first, and so which `last_n`
        keeps. The newest 1,000 documents in `window` are sorted, never more:
        a document outside them is not selected whatever its value, and the
        relation's entry renders `range_full: true` when more matched.
        Values sort by type first, numbers, then strings, then booleans,
        then missing values and anything else; `desc` reverses the order
        within each type, never the types. Numbers compare by value (whole
        numbers exactly), strings by Unicode code point, and `false` comes
        before `true`. Ties go to the most recently created, then the lower
        `id`. `records` render in this order and, over the token limit, the
        ones that sort last are dropped first; `latest` stays the most
        recently created document's value. Ordered by anything but
        `created_at`, a write to the ordered field can move a document into
        or out of the selection. The relation reads up to 1,000 documents
        per judged document to sort them; the create response's warnings
        name the factor. On a named list it sorts the documents the list
        names in place of list order, `created_at` included. Refused on a
        blocking relation and on one that reads a single referenced
        document.
      additionalProperties: false
      required:
        - by
      properties:
        by:
          type: string
          description: >-
            `created_at`, `updated_at`, a `state.<path>` or an
            `attributes.<name>`.
          pattern: ^(created_at|updated_at|state(\.[^.]+)+|attributes\.[A-Za-z0-9_-]+)$
        desc:
          type: boolean
          default: true
          description: Largest first within each type.
    AttributeFilterValue:
      type:
        - string
        - number
        - boolean
    AnswerCut:
      type: object
      description: >-
        An inline cut on an answer's `p` or `score`, exactly as stable as a
        named threshold. One bound.
      additionalProperties: false
      minProperties: 1
      maxProperties: 1
      properties:
        gte:
          type: number
        gt:
          type: number
        lte:
          type: number
        lt:
          type: number
    OutcomeExists:
      type: object
      description: >-
        Selects documents that have an outcome a relation reads, on an
        `outcomes.<j>.value` key of `match`.
      additionalProperties: false
      required:
        - exists
      properties:
        exists:
          const: true
    ReferringJoin:
      type: object
      description: |
        The documents that point at the judged one: a
        related document belongs to the judged document whose `id` equals its
        `theirs` attribute.
      additionalProperties: false
      required:
        - theirs
        - mine
      properties:
        theirs:
          type: string
          description: >-
            The related document's attribute that holds the judged document's
            id, such as `attributes.parent_id`.
          pattern: ^attributes\.[A-Za-z0-9_-]+$
        mine:
          const: id
    ReferencedJoin:
      type: object
      description: >
        The one document the judged document points at, its referenced document:
        the document whose `id` equals the

        judged document's `mine` attribute. A change to what the relation

        renders of it re-judges the judged documents that point at it and are

        inside `freshness.fanout.scope`.
      additionalProperties: false
      required:
        - theirs
        - mine
      properties:
        theirs:
          const: id
        mine:
          type: string
          description: >-
            The judged document's attribute that holds the referenced document's
            id, such as `attributes.group_id`.
          pattern: ^attributes\.[A-Za-z0-9_-]+$
    BlockingJoin:
      type: object
      description: >
        The documents that share the judged document's key value: `theirs` and
        `mine` are the same attribute, one you

        write, such as a normalised counterparty. Every document with key

        value k belongs to block k. A write to one makes the block's judged

        documents `pending`; the block is scanned at its debounce, and only

        a change to what the relation renders of the block re-judges them.

        The key should encode what a match requires. The server refuses two

        different attributes with `invalid_request`.
      additionalProperties: false
      required:
        - theirs
        - mine
      properties:
        theirs:
          type: string
          description: The key attribute, such as `attributes.block`.
          pattern: ^attributes\.[A-Za-z0-9_-]+$
        mine:
          type: string
          description: The same attribute as `theirs`.
          pattern: ^attributes\.[A-Za-z0-9_-]+$
    LinkedJoin:
      type: object
      description: |
        A judged link: a join on what a judgment that chooses among a
        blocking relation's candidates (`options.from`) currently picks,
        `answers.<j>.value`, behind one of its thresholds. One side is `id`
        and the other `answers.<j>.value`.

        With `{"theirs": "answers.<j>.value", "mine": "id"}` the relation
        reads the documents that judgment picks the judged document for,
        such as the payments a matcher linked to an invoice or the tickets
        it routed to an account. A document links while it matches the
        relation's `match`, shares the judged document's key value, and
        that judgment's current answer for it names the judged document
        below the threshold `when` names. The relation's `match` must be
        covered by that judgment's `applies_to`. When the judged document's
        block holds more documents than that judgment's `block_cap`, its
        answer keeps its last value and reads `stale` until the block is
        back within the cap or the cap is raised.

        With `{"theirs": "id", "mine": "answers.<j>.value"}` it reads the
        one document that judgment picks for the judged document, such as
        the invoice a matcher matched a payment to: while that judgment
        judges the judged document, its current answer names the document
        below the threshold `when` names, and the document is live, matches
        the relation's `match` and shares the judged document's key. It
        takes neither `last_n` nor `window`, and the judged document is
        re-judged when its pick moves and when a write changes what the
        relation shows of the document picked.

        A link is that judgment's current inference: it ends when a
        document leaves `match`, its key moves, the answer moves or the
        threshold moves. A match you record on a document is read by an
        ordinary relation on that attribute. The relation may read other
        judgments' answers on the linked documents, but nothing else of the
        judgment it joins on.
      additionalProperties: false
      required:
        - theirs
        - mine
        - when
      properties:
        theirs:
          type: string
          description: >-
            `answers.<j>.value` of the judgment that picks, which must choose
            among a blocking relation's candidates, or `id`.
          pattern: ^(id|answers\.[A-Za-z0-9_-]+\.value)$
        mine:
          type: string
          description: >-
            `id`, or `answers.<j>.value` of the judgment that picks; the other
            side of `theirs`.
          pattern: ^(id|answers\.[A-Za-z0-9_-]+\.value)$
        when:
          $ref: '#/components/schemas/LinkCondition'
    CountWhere:
      type: object
      description: |
        How many selected documents match every key: a
        judgment's `answers.<j>.value` or
        `answers.<j>.thresholds.<name>` by equality (a list is `In`), or
        its `.p` or `.score` by an inline cut; or a document's newest
        outcome, `outcomes.<j>.value`, by equality. Rendered as
        `count_where`.
      minProperties: 1
      maxProperties: 8
      propertyNames:
        type: string
        pattern: >-
          ^(answers\.[A-Za-z0-9_-]+\.(value|p|score|thresholds\.[A-Za-z0-9_-]+)|outcomes\.[A-Za-z0-9_-]+\.value)$
      additionalProperties:
        oneOf:
          - $ref: '#/components/schemas/AttributeFilterValue'
          - type: array
            minItems: 1
            items:
              $ref: '#/components/schemas/AttributeFilterValue'
          - $ref: '#/components/schemas/AnswerCut'
    SummedPaths:
      type: array
      description: >-
        Paths for `sum` or `avg`; also a judgment's `answers.<j>.p`, `.score` or
        `.dist.<option>`, as a feature only.
      minItems: 1
      maxItems: 8
      uniqueItems: true
      items:
        type: string
        description: >-
          A `state` or `attributes` path, without `(` or `)`, or an answer's
          number.
        pattern: >-
          ^((state|attributes)(\.[^.()]+)+|answers\.[A-Za-z0-9_-]+\.(p|score|dist\.[^()]+))$
    ExtremePaths:
      type: array
      description: >-
        Paths for `min` or `max`; an entry may be banded, which an answer's `p`
        or `score` must be.
      minItems: 1
      maxItems: 8
      items:
        oneOf:
          - type: string
            description: >-
              A `state` or `attributes` path. Keys in it cannot contain `(` or
              `)`.
            pattern: ^(state|attributes)(\.[^.()]+)+$
          - $ref: '#/components/schemas/BandedField'
    LatestPaths:
      type: array
      description: >-
        Paths for `latest`; also a judgment's `answers.<j>.value` or
        `answers.<j>.thresholds.<name>`, or a document's newest outcome,
        `outcomes.<j>.value`.
      minItems: 1
      maxItems: 8
      uniqueItems: true
      items:
        type: string
        description: >-
          A `state` or `attributes` path, an answer's `value` or
          `thresholds.<name>`, or an outcome's `value`. Keys in it cannot
          contain `(` or `)`.
        pattern: >-
          ^((state|attributes)(\.[^.()]+)+|answers\.[A-Za-z0-9_-]+\.(value|thresholds\.[A-Za-z0-9_-]+)|outcomes\.[A-Za-z0-9_-]+\.value)$
    LinkCondition:
      type: object
      description: |
        When a judged link holds: while the named threshold of the judgment
        that picks, a threshold on `none_of_the_above` (its unmatched
        queue), is `false`. At or above it, the pick is not taken and the
        document does not link. Required. Removing that threshold while the
        relation exists is `conflict`; changing its value re-judges each
        judged document whose links it moves, after the `downstream`
        confirm.
      additionalProperties: false
      required:
        - below
      properties:
        below:
          $ref: '#/components/schemas/Name'
    Name:
      type: string
      description: >-
        A judgment, attribute or threshold name. Names are path segments in
        field references, so they never contain `.`.
      pattern: ^[A-Za-z0-9_-]+$
      minLength: 1
      maxLength: 128
  responses:
    InvalidRequest:
      description: '`invalid_request`: the request is malformed or breaks a rule of the API.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: '`unauthorized`: the key is missing, unknown or revoked.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >-
        `forbidden`: the key's role or namespace prefix does not allow this. A
        write is also `forbidden`, with `details.reason: "payment_overdue"`,
        while a payment has been unpaid for 14 days: every key of the
        organization is read-only until it is paid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: '`not_found`: the namespace, document, judgment or job does not exist.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: >-
        `conflict`: the namespace is being deleted, the data a read needs was
        reorganized under it and the read (or the query, without its cursor)
        must restart, or the judgment is inherited from a template and this
        namespace cannot create, activate or delete it.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: >-
        `rate_limited`: slow down. `Retry-After` says for how long, except when
        a daily allowance is used up: then `details.resets_at` says when it
        renews, and there is no `Retry-After`. A read refused because its
        namespace already has as many gets and queries in flight as it allows
        says so in `details.concurrent_reads` (the limit, 32), and can come with
        rate-limit headers that show most of the per-second budget left.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Internal:
      description: >-
        `internal`: something failed on our side. Nothing partial is returned,
        and a request never commits part of itself. Most `internal` errors
        committed nothing. A write can instead fail with a message saying it may
        have been committed, when we could not tell whether its batch landed: it
        then committed all of it or none of it. Retrying a write is safe either
        way, because writes are idempotent. The message is generic and carries a
        request id, also in `details.request_id`, which support uses to find the
        cause.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: |
        An organization API key. Keys carry a role (`read_write` or
        `read_only`) and may be restricted to a namespace prefix such as
        `acme/*`, or to one namespace such as `acme/prod/tenant_1`. A prefix
        matches on a `/` boundary: `acme/prod/tenant_1*` covers
        `acme/prod/tenant_1` and everything under `acme/prod/tenant_1/`,
        never `acme/prod/tenant_12`.

````

## Related topics

- [Finding your relations](/finding-relations.md)
- [Suggest sub-questions for a composite judgment](/api-reference/judgments/suggest-sub-questions-for-a-composite-judgment.md)
- [Relations](/concepts/relations.md)
- [Discover new options](/guides/discovery.md)
- [Propose options for what the escape option holds](/api-reference/judgments/propose-options-for-what-the-escape-option-holds.md)


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