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

# Create a judgment, or a new version of an existing name

> Posting a name that exists creates version `n+1`; versions are
immutable. The first version of a name is active on creation; a later
version is inactive until activated, unless `activate: true`, which
implies `force` and skips the shadow report.

Freshness settings belong to the judgment, not the version:
`freshness` sets them with the name's first version. A later version
keeps the judgment's settings, and `warnings` says so when the
`freshness` sent differs from them. Change them with `PATCH`, which
asks for `confirm` before a switch to `on_change`.

Thresholds are a setting of the judgment, not part of the version. Thresholds given here replace the judgment's thresholds when
this version becomes active, because they are written for its type; a
version created without them keeps the current thresholds. After that,
change them with `PATCH`.

On a prefix path (`acme%2Fprod%2F*`) this creates a template that every
namespace under the prefix inherits. A namespace cannot create a
judgment with the name of one it inherits (`conflict`). Templates need
the Team plan or above: on a prefix path, creating a version, `PATCH`
and activating are `plan_required` below it, while reading, backfill,
delete and detach work on every plan.

A composite judgment (a `bool` with `parts`) is never active on
creation, not even as the first version, and `activate: true` is
`invalid_request` for one: it cannot answer until its combiner is
fitted on your labels by the shadow job that activation starts.

An entity judgment has `context.related`.
Creating a version of one on a judgment whose policy will be
`on_change` needs `confirm: true`. Without it, the response is `200`
with the replay estimate of its monthly cost, and nothing is created. A confirmed request whose estimate is more than the
namespace's budget is `budget_exceeded`. When the version
joins on attributes with no reference index yet, `job_ids` names the
`reference_index` job that builds each one, and `job_id` the first.
A ninth reference index in a namespace is
`invalid_request`, and `details.reference_indexes` names the current
ones.

A relation that reads a referenced document joins with
`{theirs: "id", mine: "attributes.<name>"}`. It needs a reference
index on the judged documents' `mine` attribute, built by a
`reference_index` job like any other and counted toward the same 8.
Its replay estimate cannot count fan-out, so it has
`excludes: ["fanout"]` and `lower_bound: true`, and without `confirm`
the response's `fanout` shows the rolling limit the confirm sets. A
join on two different attributes is `invalid_request`, and
`warnings` names a relation that renders a referenced document's
`updated_at`, which changes on every write to it.

A blocking relation joins on one key attribute
on both sides, and a `choice` can choose among it with
`options.from`; `context.previous` reads the document as the
judgment last saw it. A judgment with a blocking relation needs
`confirm` like one with a referenced relation, and a key value over
`block_cap` at create is `invalid_request` naming it in
`details.key`.

A definition needs a context recipe. Leaving out
`context` is `invalid_request`, and `details.suggested_context` is a
recipe suggested from up to 100 of the namespace's documents, with
its cost per answer beside the whole `state`'s (null when there are
no documents). Send it back as `context`, or `use_context:
"suggested"` to create with it, or `use_context: "whole_state"` to
send the whole `state`.

A starter (`GET /starters`) is created with
`from_starter` and `paths`: the server fills the starter's
placeholders with your paths and validates the result like any
definition. The `201` carries `definition`, exactly what was
created, and for a starter with a suggested outcome setting,
`outcomes`: applied with the name's first version, its rules only on
a plan with outcome rules.

`dry_run: true` validates the body as a create would and returns the
definition with its cost per answer and backfill estimate (`200`);
nothing is created.




## OpenAPI

````yaml /api-reference/openapi.yaml post /namespaces/{ns}/judgments
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 plain judgments' answers (banded or
      cut, never summed), 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}/judgments:
    parameters:
      - $ref: '#/components/parameters/NamespaceOrTemplate'
    post:
      tags:
        - Judgments
      summary: Create a judgment, or a new version of an existing name
      description: >
        Posting a name that exists creates version `n+1`; versions are

        immutable. The first version of a name is active on creation; a later

        version is inactive until activated, unless `activate: true`, which

        implies `force` and skips the shadow report.


        Freshness settings belong to the judgment, not the version:

        `freshness` sets them with the name's first version. A later version

        keeps the judgment's settings, and `warnings` says so when the

        `freshness` sent differs from them. Change them with `PATCH`, which

        asks for `confirm` before a switch to `on_change`.


        Thresholds are a setting of the judgment, not part of the version.
        Thresholds given here replace the judgment's thresholds when

        this version becomes active, because they are written for its type; a

        version created without them keeps the current thresholds. After that,

        change them with `PATCH`.


        On a prefix path (`acme%2Fprod%2F*`) this creates a template that every

        namespace under the prefix inherits. A namespace cannot create a

        judgment with the name of one it inherits (`conflict`). Templates need

        the Team plan or above: on a prefix path, creating a version, `PATCH`

        and activating are `plan_required` below it, while reading, backfill,

        delete and detach work on every plan.


        A composite judgment (a `bool` with `parts`) is never active on

        creation, not even as the first version, and `activate: true` is

        `invalid_request` for one: it cannot answer until its combiner is

        fitted on your labels by the shadow job that activation starts.


        An entity judgment has `context.related`.

        Creating a version of one on a judgment whose policy will be

        `on_change` needs `confirm: true`. Without it, the response is `200`

        with the replay estimate of its monthly cost, and nothing is created. A
        confirmed request whose estimate is more than the

        namespace's budget is `budget_exceeded`. When the version

        joins on attributes with no reference index yet, `job_ids` names the

        `reference_index` job that builds each one, and `job_id` the first.

        A ninth reference index in a namespace is

        `invalid_request`, and `details.reference_indexes` names the current

        ones.


        A relation that reads a referenced document joins with

        `{theirs: "id", mine: "attributes.<name>"}`. It needs a reference

        index on the judged documents' `mine` attribute, built by a

        `reference_index` job like any other and counted toward the same 8.

        Its replay estimate cannot count fan-out, so it has

        `excludes: ["fanout"]` and `lower_bound: true`, and without `confirm`

        the response's `fanout` shows the rolling limit the confirm sets. A

        join on two different attributes is `invalid_request`, and

        `warnings` names a relation that renders a referenced document's

        `updated_at`, which changes on every write to it.


        A blocking relation joins on one key attribute

        on both sides, and a `choice` can choose among it with

        `options.from`; `context.previous` reads the document as the

        judgment last saw it. A judgment with a blocking relation needs

        `confirm` like one with a referenced relation, and a key value over

        `block_cap` at create is `invalid_request` naming it in

        `details.key`.


        A definition needs a context recipe. Leaving out

        `context` is `invalid_request`, and `details.suggested_context` is a

        recipe suggested from up to 100 of the namespace's documents, with

        its cost per answer beside the whole `state`'s (null when there are

        no documents). Send it back as `context`, or `use_context:

        "suggested"` to create with it, or `use_context: "whole_state"` to

        send the whole `state`.


        A starter (`GET /starters`) is created with

        `from_starter` and `paths`: the server fills the starter's

        placeholders with your paths and validates the result like any

        definition. The `201` carries `definition`, exactly what was

        created, and for a starter with a suggested outcome setting,

        `outcomes`: applied with the name's first version, its rules only on

        a plan with outcome rules.


        `dry_run: true` validates the body as a create would and returns the

        definition with its cost per answer and backfill estimate (`200`);

        nothing is created.
      operationId: createJudgment
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJudgmentRequest'
            example:
              name: needs_escalation
              type: bool
              question: >-
                Does this ticket require a human to take over from the automated
                flow?
              criteria: >-
                Escalate when the customer is at risk of leaving, mentions legal
                action, or the automation has failed twice.
              context:
                fields:
                  - state.subject
                  - state.body
                  - attributes.plan
                last_n:
                  state.messages: 3
                window:
                  state.events: 7d
                max_tokens: 4000
              engine:
                name: jev
                version: current
              freshness:
                policy: on_change
                debounce_ms: 0
              thresholds:
                escalate: 0.85
              activate: true
      responses:
        '200':
          description: |
            Nothing was created: a dry run (`dry_run: true`), or an entity
            judgment that needs `confirm` and was not confirmed, whose replay
            estimate this is.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CreateJudgmentDryRun'
                  - $ref: '#/components/schemas/CreateJudgmentEstimate'
        '201':
          description: The new version.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateJudgmentResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/CreateJudgmentConflict'
        '413':
          $ref: '#/components/responses/TooLarge'
        '422':
          $ref: '#/components/responses/EngineVersionUnavailable'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Internal'
        '503':
          $ref: '#/components/responses/CreateJudgmentUnavailable'
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'
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: |
        One key per logical request, reused only on its retries. A key
        belongs to one request: within your organization, the same method,
        path, query and body. For 24 hours after a successful response, a
        request with the key and the same body gets that response back
        verbatim, with `Idempotent-Replayed: true`, and runs nothing. Only a
        successful response is kept, so the retry of a request that failed
        runs again. While the first request runs or its response is kept,
        the key with a different request is `idempotency_key_reused` (422).
        A request sent while one with its key is still running is
        `rate_limited` with `Retry-After: 1`, without running: retry it to
        get the first one's response. In the rare case the key can't be
        checked, the request runs as if it had none.
      schema:
        type: string
        minLength: 1
        maxLength: 255
  schemas:
    CreateJudgmentRequest:
      description: A definition, or a starter filled with your paths.
      oneOf:
        - $ref: '#/components/schemas/CreateJudgmentFromDefinition'
        - $ref: '#/components/schemas/CreateJudgmentFromStarter'
    CreateJudgmentDryRun:
      type: object
      description: |
        What a create would make, and what it would cost.
        Nothing was created, written or billed.
      required:
        - dry_run
        - definition
        - warnings
        - cost_per_answer
        - sampled_documents
        - estimate
        - replay
        - outcomes
      properties:
        dry_run:
          const: true
        definition:
          $ref: '#/components/schemas/JudgmentDefinition'
        warnings:
          type: array
          items:
            type: string
        cost_per_answer:
          type:
            - number
            - 'null'
          minimum: 0
          description: |
            Mean judgments per answer over `sampled_documents` of the
            documents the definition applies to, each counted by its size
            class as a backfill estimate counts them: 1 when every answer is
            standard, 4 when every one is large. Null when there is nothing
            to sample, and on a template's prefix path.
        sampled_documents:
          type: integer
          minimum: 0
        estimate:
          description: |
            What backfilling every document the definition applies to would
            cost once it exists. Creating never backfills by itself. Null on
            a template's prefix path.
          oneOf:
            - $ref: '#/components/schemas/Estimate'
            - type: 'null'
        replay:
          description: >-
            An entity judgment's monthly replay estimate, whatever its policy;
            null for others.
          oneOf:
            - $ref: '#/components/schemas/ReplayEstimate'
            - type: 'null'
        outcomes:
          description: >-
            For a starter with a suggested outcome setting, what the create
            would apply; null otherwise.
          oneOf:
            - $ref: '#/components/schemas/StarterOutcomes'
            - type: 'null'
    CreateJudgmentEstimate:
      type: object
      description: >-
        An unconfirmed create of an entity judgment that runs `on_change`.
        Nothing was created.
      required:
        - replay
      not:
        description: A dry run is `CreateJudgmentDryRun`, even with a replay estimate.
        required:
          - dry_run
        properties:
          dry_run:
            const: true
      properties:
        replay:
          $ref: '#/components/schemas/ReplayEstimate'
        fanout:
          $ref: '#/components/schemas/FanoutLimitEstimate'
    CreateJudgmentResponse:
      type: object
      required:
        - name
        - version
        - active
        - warnings
        - definition
      properties:
        name:
          $ref: '#/components/schemas/Name'
        version:
          $ref: '#/components/schemas/JudgmentVersionNumber'
        active:
          type: boolean
        warnings:
          type: array
          description: >-
            Non-fatal problems, such as sending the whole `state`, a recipe
            suggested for you, or a relation that renders a referenced
            document's `updated_at`, which changes on every write to it and so
            fans out every time.
          items:
            type: string
        definition:
          $ref: '#/components/schemas/JudgmentDefinition'
          description: The version exactly as created, with the engine resolved.
        outcomes:
          $ref: '#/components/schemas/StarterOutcomes'
          description: From a starter with a suggested outcome setting only.
        job_id:
          type: string
          description: >-
            The first of `job_ids`, kept for clients written before `job_ids`.
            Read `job_ids` to see every job.
        job_ids:
          type: array
          minItems: 1
          description: >-
            The `reference_index` jobs that build the indexes this version's
            relations need, one per attribute with no index yet, or whose index
            must be rebuilt to cover this version's documents (another `match`,
            or the attribute used the other way round), so two when it joins on
            two new attributes. Present exactly when `job_id` is. Until every
            job is done, the version's answers are `unavailable`, or `pending`
            for documents written since that an `on_change` judgment judges once
            the jobs are done.
          items:
            type: string
    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
    CreateJudgmentFromDefinition:
      type: object
      description: >-
        A definition plus, for the name's first version, the judgment's
        freshness settings.
      allOf:
        - $ref: '#/components/schemas/JudgmentDefinition'
      properties:
        freshness:
          $ref: '#/components/schemas/FreshnessSettings'
        activate:
          type: boolean
          description: >-
            Activate this version on creation. In v1 this implies `force`.
            Refused for a composite judgment, which activation fits on your
            labels first.
        confirm:
          type: boolean
          default: false
          description: >
            Required to create a version with `context.related` on a judgment
            whose policy will be `on_change`. Without it, the

            response is the replay estimate of the monthly cost, and nothing

            is created; for a judgment that can fan out it also carries

            `fanout`, the rolling limit the confirm sets. Ignored for other

            judgments.
        use_context:
          type: string
          enum:
            - suggested
            - whole_state
          description: |
            Instead of `context`: `suggested` creates with the
            recipe suggested from a sample of the namespace's documents, the
            one a create without `context` returns in
            `details.suggested_context`; `whole_state` sends the whole
            `state`, with a warning. Refused together with `context`.
        dry_run:
          $ref: '#/components/schemas/DryRunFlag'
      unevaluatedProperties: false
    CreateJudgmentFromStarter:
      type: object
      description: |
        A starter from `GET /starters`, filled with your paths.
        The starter sets the question, criteria, type, options or levels,
        recipe, thresholds, `applies_to` and `horizon`; to change them, take
        the `definition` a dry run returns, edit it and create from it.
      additionalProperties: false
      required:
        - from_starter
      properties:
        from_starter:
          type: string
          description: A starter's `id`, such as `ticket_triage.urgency`.
          examples:
            - ticket_triage.urgency
        paths:
          type: object
          description: |
            Each placeholder's name to your path, such as
            `{"subject": "state.subject"}`, or for a `value` placeholder the
            value your documents hold. Every placeholder that is not
            `optional` is required; an optional one left out removes what
            it fills.
          additionalProperties:
            type: string
            minLength: 1
        name:
          $ref: '#/components/schemas/Name'
          description: The judgment's name; the starter's by default.
        engine:
          $ref: '#/components/schemas/EngineRef'
          description: >-
            Must be `active` in the registry. May be omitted only when the
            namespace has a `default_engine`.
        freshness:
          $ref: '#/components/schemas/FreshnessSettings'
        activate:
          type: boolean
          description: As on any create.
        confirm:
          type: boolean
          default: false
          description: As on any create; the churn risk starter is an entity judgment.
        dry_run:
          $ref: '#/components/schemas/DryRunFlag'
    JudgmentDefinition:
      type: object
      description: |
        A judgment definition, discriminated on `type`. `thresholds` are the
        named thresholds given with this version. They are not part of the
        version: the judgment's current thresholds are its `thresholds`
        setting, evaluated at read time and returned as booleans under
        `answers.{name}.thresholds`.
      oneOf:
        - $ref: '#/components/schemas/BoolJudgmentDefinition'
        - $ref: '#/components/schemas/ChoiceJudgmentDefinition'
        - $ref: '#/components/schemas/ScoreJudgmentDefinition'
      discriminator:
        propertyName: type
        mapping:
          bool:
            $ref: '#/components/schemas/BoolJudgmentDefinition'
          choice:
            $ref: '#/components/schemas/ChoiceJudgmentDefinition'
          score:
            $ref: '#/components/schemas/ScoreJudgmentDefinition'
    Estimate:
      type: object
      required:
        - documents
        - tokens
        - judgment_units
        - cost_usd
        - duration_s
      properties:
        documents:
          type: integer
          format: int64
          minimum: 0
        tokens:
          type: integer
          format: int64
          minimum: 0
        judgment_units:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Estimated judgments, each counted by its size class, from a sample
            of up to 1,000 documents.
        cost_usd:
          type: number
          minimum: 0
          description: >-
            What the backfill is expected to cost you, the judgments at the
            graduated prices of the tiers your organization will be in, counting
            what it has already used this billing period.
        duration_s:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Expected wall-clock seconds if nothing else is judged meanwhile.
            Other judging comes first (new writes, catch-ups, other backfills)
            and makes it longer, so read it as the least to expect.
        downstream:
          type: array
          description: >-
            A backfill of a judgment whose answers other judgments read also
            re-judges them where a rendered band or cut flips; one line per
            reader. Absent when it has none.
          items:
            $ref: '#/components/schemas/DownstreamLine'
    ReplayEstimate:
      type: object
      description: >
        What an entity judgment would cost a month, from the

        namespace's last 30 days of writes run through its relations,

        debounce and ceiling rules. The replay cannot see every edit to a

        related document, only its creation, its latest write and the

        deletes still recorded: exact for documents written once, and for

        often-edited documents the figures are a lower bound

        (`lower_bound`). It does not count the evaluations an unchanged context
        saves, which only lower the bill.
      required:
        - replayed_days
        - entities
        - judgments_per_month
        - judgment_units_per_month
        - cost_usd_per_month
        - bulk_pool_share
        - lower_bound
      properties:
        replayed_days:
          type: integer
          minimum: 0
          maximum: 30
          description: >-
            Days of writes replayed, fewer than 30 in a younger namespace. The
            monthly figures are scaled from them to 30 days.
        entities:
          type: integer
          format: int64
          minimum: 0
          description: Documents the judgment applies to now.
        judgments_per_month:
          type: integer
          format: int64
          minimum: 0
          description: Evaluations the replay counted, scaled to 30 days.
        judgment_units_per_month:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Judgments, each counted by its size class, from the compiled context
            and question of a sample of up to 1,000 entities.
        cost_usd_per_month:
          type: number
          minimum: 0
          description: >-
            The judgments at the graduated prices of the tiers your organization
            will be in, counting what it has already used this billing period. A
            confirmed request is refused with `budget_exceeded` when this is
            more than the namespace's budget.
        bulk_pool_share:
          type:
            - number
            - 'null'
          minimum: 0
          description: >-
            The share of the background judging rate available to this judgment
            that these evaluations would use, by requests or tokens, whichever
            is larger. Above 1, the judgment cannot keep up with the writes.
            Null when the engine has no rate limit.
        lower_bound:
          type: boolean
          description: >-
            True when a related document in the replayed window has a `revision`
            higher than the writes the replay saw, so it was edited more often
            than the replay counted, and whenever `excludes` is present. The
            figures are then a lower bound, shown as "at least". The namespace
            budget still caps real spend.
        excludes:
          type: array
          description: >
            What the figures leave out, present only when something is.
            `fanout`: the judgment has a relation that

            reads a referenced document, and the replay cannot tell which past

            writes to it changed what the relation shows, so it cannot count

            its fan-out; `freshness.fanout.rolling_limit` bounds it.
            `candidate_edits` and `candidate_deletes`: a blocking

            relation's candidates edited or deleted in the window, which leave

            nothing a replay can read.
          minItems: 1
          uniqueItems: true
          items:
            type: string
            enum:
              - fanout
              - candidate_edits
              - candidate_deletes
    StarterOutcomes:
      type: object
      description: |
        A starter's suggested outcome setting and what the create did with
        it, or in a dry run what it would do. It is applied with
        the name's first version in a namespace: implicit negatives on every
        plan, outcome rules only on a plan with outcome rules.
      required:
        - suggested
        - applied
        - plan_required
        - message
      properties:
        suggested:
          $ref: '#/components/schemas/OutcomeSettings'
        applied:
          description: >-
            The setting now on the judgment, or in a dry run the one the create
            would apply; null when nothing is applied.
          oneOf:
            - $ref: '#/components/schemas/OutcomeSettings'
            - type: 'null'
        plan_required:
          description: Why the rules were not applied, when the plan lacks outcome rules.
          oneOf:
            - $ref: '#/components/schemas/OutcomeRulesPlanRequired'
            - type: 'null'
        message:
          type: string
          description: >-
            What happened (in a dry run, what would happen), and what to do, in
            a sentence.
    FanoutLimitEstimate:
      type: object
      description: |
        For a judgment that can fan out: the rolling limit
        `confirm: true` sets, from `freshness.fanout` in the request or the
        default of 300,000, and what it costs if used in full. Work past it is
        deferred at runtime, never put to you again. Set
        `freshness.fanout.rolling_limit` to `null` to confirm with no limit:
        every change then re-judges every in-scope document, with no
        deferral, and only the namespace's budget caps what fan-out
        spends.
      required:
        - rolling_limit
        - cost_usd_at_limit
      properties:
        rolling_limit:
          type:
            - integer
            - 'null'
          format: int64
          minimum: 0
          description: >-
            Re-judgments by referenced and block changes allowed in any 30 days;
            null for no limit.
        cost_usd_at_limit:
          type:
            - number
            - 'null'
          minimum: 0
          description: >-
            What `rolling_limit` re-judgments cost in 30 days at the sample's
            judgments per answer and your organization's tiers; null with no
            limit.
    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
    JudgmentVersionNumber:
      type: integer
      format: int32
      minimum: 1
    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'
    FreshnessSettings:
      type: object
      description: Settings, not part of the definition. Changing them creates no version.
      additionalProperties: false
      properties:
        policy:
          $ref: '#/components/schemas/FreshnessPolicy'
        debounce_ms:
          type: integer
          minimum: 0
          default: 0
          description: >-
            A document changed more recently than this is not judged until it
            settles.
        interval:
          $ref: '#/components/schemas/Duration'
          description: >-
            Recompute interval for `periodic`, such as `1h` or `1d`. At least
            `1h`, because each run re-judges the whole namespace; a shorter one
            is `invalid_request`.
        max_wait_ms:
          type:
            - integer
            - 'null'
          minimum: 1
          description: |
            The debounce ceiling: a document that keeps
            changing is still judged once this long has passed since its
            oldest unjudged change. At least `debounce_ms`; `null` means no
            ceiling. Defaults to 12 × `debounce_ms` for an entity judgment with
            a `debounce_ms` above 0, and to no ceiling otherwise: with no
            debounce a document is judged at once. `GET` returns the value in
            effect.
        fanout:
          $ref: '#/components/schemas/FanoutSettings'
      if:
        required:
          - policy
        properties:
          policy:
            const: periodic
      then:
        required:
          - interval
    DryRunFlag:
      type: boolean
      default: false
      description: |
        Validate the body as a create would, and return the definition with
        its cost per answer and backfill estimate, creating nothing
        (`CreateJudgmentDryRun`).
    EngineRef:
      type: object
      description: >
        An engine and its version, which must be active in `GET /engines`: an

        exact version, or Jev's `current`, which runs whatever model the

        provider serves now and records the epoch of behaviour behind each

        answer in its `engine_version`, such as `current+2026-09-24.1`. There
        are no other aliases.
      required:
        - name
        - version
      properties:
        name:
          type: string
          enum:
            - jev
            - laya
        version:
          type: string
          minLength: 1
    BoolJudgmentDefinition:
      type: object
      description: |
        The answer has `p`. With `parts` it is a composite judgment:
        the engine is asked each part instead of `question`, and `p` combines
        the parts' answers with weights fitted on the judgment's outcomes.
      required:
        - type
      allOf:
        - $ref: '#/components/schemas/JudgmentDefinitionCommon'
      dependentRequired:
        features:
          - parts
      not:
        description: A single part needs features.
        required:
          - parts
        properties:
          parts:
            type: array
            maxItems: 1
        not:
          required:
            - features
      properties:
        type:
          const: bool
        thresholds:
          type: object
          description: Each threshold is true when `p` is at least the value.
          propertyNames:
            $ref: '#/components/schemas/Name'
          additionalProperties:
            $ref: '#/components/schemas/Probability'
        parts:
          type: array
          description: |
            Composite judgments only: 2 to 8 narrow yes/no questions with
            names unique within the judgment. They share the judgment's
            context recipe and engine, so they go in one engine request with
            its other questions, and each counts toward the 32 questions per
            request. `question` then documents what the combination means and
            is not sent to the engine. Parts are part of the version. They
            cannot reference other judgments. Each part is billed as a
            judgment. With `features`, one part is enough.
          minItems: 1
          maxItems: 8
          items:
            $ref: '#/components/schemas/JudgmentPart'
        features:
          type: array
          description: |
            Composite judgments only: aggregates over
            related documents that the combiner takes as numeric inputs beside
            the parts. Each names an aggregate the recipe's `related`
            declares. Features are scaled automatically beside the parts; a
            missing value counts as typical. Features add no questions, so
            they are never billed.
          minItems: 1
          maxItems: 8
          uniqueItems: true
          items:
            $ref: '#/components/schemas/FeatureRef'
    ChoiceJudgmentDefinition:
      type: object
      description: |
        The answer has `value`, `dist` and `escape_p`. The API appends
        `none_of_the_above` as a final option on every choice judgment.
      required:
        - type
        - options
      allOf:
        - $ref: '#/components/schemas/JudgmentDefinitionCommon'
      properties:
        type:
          const: choice
        options:
          description: >-
            The options, whose values must be distinct; or `{from, label}`, one
            option per document a blocking relation of the recipe selects.
          oneOf:
            - type: array
              minItems: 2
              maxItems: 254
              items:
                $ref: '#/components/schemas/ChoiceOption'
            - $ref: '#/components/schemas/OptionsFrom'
        thresholds:
          type: object
          description: Each threshold is true when `dist[value]` is at least `gte`.
          propertyNames:
            $ref: '#/components/schemas/Name'
          additionalProperties:
            $ref: '#/components/schemas/ChoiceThreshold'
    ScoreJudgmentDefinition:
      type: object
      description: >-
        The answer has `score`, the probability-weighted mean of level values,
        and `dist`.
      required:
        - type
        - levels
      allOf:
        - $ref: '#/components/schemas/JudgmentDefinitionCommon'
      properties:
        type:
          const: score
        levels:
          type: array
          description: Ordered; level values must be distinct.
          minItems: 2
          maxItems: 10
          items:
            $ref: '#/components/schemas/ScoreLevel'
        thresholds:
          type: object
          description: Each threshold is true when `score` is at least the value.
          propertyNames:
            $ref: '#/components/schemas/Name'
          additionalProperties:
            type: number
    DownstreamLine:
      type: object
      description: >
        One reader's cost of a change to a judgment whose answers its relations
        read: the re-judgments the change would cause

        a month (for a one-off such as a backfill, in the month it runs),

        counted from the answer history's band and cut flips over the last

        30 days, priced by the reader's size class at your tiers.
      required:
        - judgment
        - judgments_per_month
        - cost_usd
      properties:
        judgment:
          $ref: '#/components/schemas/Name'
          description: The reader, a judgment whose relation reads this one's answers.
        judgments_per_month:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Evaluations of the reader the change would cause a month. It is
            exact except for a child that moved between parents in the window,
            which the history does not record.
        cost_usd:
          type: number
          minimum: 0
          description: >-
            Those evaluations at the reader's size class and your organization's
            tiers, in US dollars.
    OutcomeSettings:
      type: object
      description: |
        How the judgment's outcomes are read and derived, a setting
        rather than part of a version. See [calibration](/concepts/calibration).
      additionalProperties: false
      properties:
        rules:
          type: array
          maxItems: 4
          items:
            $ref: '#/components/schemas/OutcomeRule'
          description: |
            Derive outcomes from the judged document's own writes, so labels
            arrive without posting them. Each rule is checked on every write
            to a document from when it is set; earlier writes are never
            replayed. Derived outcomes are stored with the write, join like
            posted ones, and a posted outcome beats a derived one in the same
            prediction window. With a `0s` horizon, a derived outcome labels
            the answer to the document as it was just before the write that
            revealed it. Omitted when there are none. Not accepted on a
            template's prefix path. Setting rules needs the Team plan or above
            (`plan_required`); clearing them never does. Outcomes derived
            while the plan does not include rules are kept but left out of
            calibration (`rule_outcomes_paused` in the report).
        implicit_negatives:
          type: boolean
          default: false
          description: |
            For a `bool` judgment with a non-zero `horizon` only. When true, a
            prediction window that has closed with no outcome counts as a
            `false` outcome, so you only need to post what happened. A window
            still open counts nothing yet, one whose document was deleted
            before it closed is left out unless an outcome labels it, and one
            opened at or after a `true` for the document is left out; the
            report counts them in `censored_predictions`. Leave it off when a
            missing outcome does not
            mean "no", for example when you only record some of what happens.
            On a namespace that inherits the judgment, set it on the
            template's prefix path: its calibration is shared.
    OutcomeRulesPlanRequired:
      type: object
      description: >-
        Why a starter's outcome rules were not applied, as `plan_required` says
        it.
      required:
        - message
        - capability
        - required_plan
        - plan
      properties:
        message:
          type: string
        capability:
          type: string
          enum:
            - outcome_rules
        required_plan:
          type: string
          enum:
            - team
            - scale
        plan:
          type: string
          enum:
            - developer
            - team
            - scale
    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
    FreshnessPolicy:
      type: string
      description: |
        `on_read` (default) computes on first read, then caches until the
        document changes; it cannot be used in a query filter or rank.
        `on_change` recomputes whenever a document changes, the policy for a
        judgment filtered or ranked on. `periodic` recomputes every
        `interval`. `manual` is backfill only. A query can filter and rank on
        `on_change`, `periodic` and `manual` answers, as stored.
      enum:
        - on_read
        - on_change
        - periodic
        - manual
    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]$
    FanoutSettings:
      type: object
      description: >
        For a judgment with a relation that reads a referenced document, or a
        blocking relation: which judged documents a change re-judges, and when.
        Only a

        change to what the relation renders counts. Every key is optional in

        a request, and `GET` returns every value in effect for a judgment

        that can fan out. In a `PATCH`, the keys you send replace those keys

        and the others keep their values.


        No fan-out waits for a confirm. You confirm the rolling

        limit once, when you create the judgment; a fan-out that would pass

        it, or a block past `block_cap`, is deferred, its answers read

        `stale` (`limit_reached`) with a `judgment.limit_reached` event, and

        it runs once the window has room or a `PATCH` raises the limit.
      additionalProperties: false
      minProperties: 1
      properties:
        scope:
          $ref: '#/components/schemas/FanoutScope'
        debounce_ms:
          type: integer
          minimum: 0
          default: 600000
          description: >-
            A referenced document that changed more recently than this is not
            fanned out until it settles, so a burst of changes to it costs one
            fan-out. Counts changes to what the relation renders, per referenced
            document (per block). The judgment's own `debounce_ms` still governs
            the judged documents' own writes.
        max_wait_ms:
          type:
            - integer
            - 'null'
          minimum: 1
          description: >-
            The ceiling. A referenced document that keeps changing still fans
            out once this long has passed since its oldest change not yet fanned
            out. At least `debounce_ms`; `null` means no ceiling. Defaults to 6
            × `debounce_ms`, one hour at the default debounce.
        rolling_limit:
          type:
            - integer
            - 'null'
          format: int64
          minimum: 0
          default: 300000
          description: >-
            The most judged documents this judgment's referenced and block
            changes re-judge in any 30 days, confirmed when you create the
            judgment. A fan-out that would take the window past it is deferred
            until the window has room. `null` removes the limit, so every change
            re-judges every in-scope document, with no deferral, and the
            namespace budget alone bounds it.
        block_cap:
          type: integer
          format: int64
          minimum: 1
          default: 1000
          description: >-
            For a blocking relation. The most judged documents one key value's
            block may hold. At create, a key value over it is `invalid_request`;
            a block that grows past it later is deferred, its answers `stale`
            (`limit_reached`) with a `judgment.limit_reached` event naming the
            key value, until the cap is raised.
    JudgmentDefinitionCommon:
      type: object
      required:
        - name
        - question
      properties:
        name:
          $ref: '#/components/schemas/Name'
        question:
          type: string
          minLength: 1
        criteria:
          type: string
        context:
          $ref: '#/components/schemas/ContextRecipe'
        engine:
          $ref: '#/components/schemas/EngineRef'
          description: >-
            Must be `active` in the registry. May be omitted only when the
            namespace has a `default_engine`.
        horizon:
          $ref: '#/components/schemas/Horizon'
          description: |
            How far before `observed_at` an outcome's prediction was made, such
            as `30d` for "churned within 30 days". Part of the definition, so
            changing it creates a version. Defaults to `0s`, which joins
            labelled examples to current answers.
        applies_to:
          $ref: '#/components/schemas/AttributeFilter'
          description: >
            The judgment judges, answers and bills only documents whose
            attributes match. A document that does not match

            has no answer for it: `answers` omits it. Part of the definition,

            so changing it creates a version.
    Probability:
      type: number
      minimum: 0
      maximum: 1
      description: >
        Answers keep probabilities to about seven significant digits and

        return them as the shortest decimal that stands for the stored value, so
        an engine's

        0.01 reads 0.01. Thresholds, query filters and threshold

        recommendations compare that decimal.
    JudgmentPart:
      type: object
      description: One part of a composite judgment.
      additionalProperties: false
      required:
        - name
        - question
      properties:
        name:
          $ref: '#/components/schemas/Name'
        question:
          type: string
          minLength: 1
          description: A narrow yes/no question about the document.
    FeatureRef:
      type: string
      description: |
        An aggregate the recipe declares, as
        `<relation>.count` or `<relation>.<sum|min|max|latest>(<path>)`, such
        as `tickets.count` or `invoices.sum(state.amount)`.
      pattern: >-
        ^[A-Za-z0-9_-]+\.(count|(sum|min|max|latest)\((state|attributes)(\.[^.()]+)+\))$
    ChoiceOption:
      type: object
      additionalProperties: false
      required:
        - value
      properties:
        value:
          type: string
          minLength: 1
          not:
            const: none_of_the_above
        description:
          type: string
    OptionsFrom:
      type: object
      description: |
        Choose among a blocking relation's documents in one call:
        one option per selected candidate, in the relation's order, `value`
        its `id` and each candidate described by its `label` fields, then
        `none_of_the_above`. `from` names a blocking relation of the recipe
        with `last_n` at most the `options.from` candidate limit (see
        Limits). A judged document with no candidates is
        answered `none_of_the_above` with `escape_p` 1 and no engine call.
        `dist` is a shortlist, not a ranking: past the first few candidates
        the probabilities tie at zero. The judgment is not plain, so no
        relation can read it, and its `horizon` must be `0s`.
      additionalProperties: false
      required:
        - from
        - label
      properties:
        from:
          $ref: '#/components/schemas/Name'
          description: A blocking relation in the recipe's `related`.
        label:
          type: array
          description: The candidate's paths that describe it to the engine, in order.
          minItems: 1
          maxItems: 8
          items:
            $ref: '#/components/schemas/RelationField'
    ChoiceThreshold:
      type: object
      additionalProperties: false
      required:
        - value
        - gte
      properties:
        value:
          type: string
          minLength: 1
        gte:
          $ref: '#/components/schemas/Probability'
    ScoreLevel:
      type: object
      additionalProperties: false
      required:
        - value
        - label
      properties:
        value:
          type: integer
        label:
          type: string
          minLength: 1
        description:
          type: string
    OutcomeRule:
      type: object
      description: >
        One outcome rule: when a write changes the value at

        `when.path` to the target, the write is an outcome with `value`, or

        the value at `from` after the write. A write that leaves the value

        where it was, a retry included, derives nothing, and a delete never

        does. A write that creates the document compares with nothing.

        Checked against the judgment's type when set: a `bool` takes `value`

        `true` or `false`, a `choice` an option and a `score` a level. A

        `from` value that does not fit (or a missing `from` path) is kept but

        adds no sample; the report counts it in

        `unmatched_outcomes.does_not_fit`.


        `on` instead of `when` fires when the value at the path

        goes from absent or null to present, such as a payment's

        `state.settles` recorded when it is matched. A rule is checked on a
        write when the document matched the judgment's

        `applies_to` before it, so the write that takes a matched document

        out of `applies_to` still produces its outcome.
      additionalProperties: false
      anyOf:
        - required:
            - when
        - required:
            - 'on'
      not:
        description: A rule has `when` or `on`, not both.
        required:
          - when
          - 'on'
      oneOf:
        - required:
            - value
        - required:
            - from
      properties:
        when:
          $ref: '#/components/schemas/OutcomeRuleCondition'
        'on':
          $ref: '#/components/schemas/DocumentPath'
          description: Fires when this path goes from absent or null to present.
        value:
          description: The outcome, a constant matching the judgment type.
          type:
            - boolean
            - string
            - integer
        from:
          $ref: '#/components/schemas/DocumentPath'
          description: >-
            For `choice` and `score` judgments, the path whose value after the
            write is the outcome, such as a ticket's final `state.team`. A
            number with no fraction reads as a level.
    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 verbatim, in order.
          items:
            type: string
            pattern: ^(state|attributes)(\.[^.]+)+$
        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'
    FanoutScope:
      type: object
      description: |
        The re-judge scope: which judged documents a
        change to a referenced document at log position c re-judges. At the
        fan-out's snapshot, a judged document is in scope when it points at
        the referenced document, matches `applies_to` and `where`, and was
        created no more than `created_within` before the write at c. A judged
        document outside the scope is not re-judged: its answer reads `stale`
        with `stale_reason` `referenced_changed` until the document is judged
        again, and its `watermark` says which version of the referenced
        document it read.
      additionalProperties: false
      minProperties: 1
      properties:
        created_within:
          description: >-
            Only judged documents created no more than this long before the
            change. `null` means all of them. Defaults to `30d`.
          oneOf:
            - $ref: '#/components/schemas/Duration'
            - type: 'null'
        where:
          description: >-
            Narrows the scope to judged documents whose attributes match, such
            as `{"attributes.status": "open"}`. `null` removes it. None by
            default.
          oneOf:
            - $ref: '#/components/schemas/AttributeFilter'
            - type: 'null'
    Horizon:
      type: string
      description: |
        How far ahead the judgment predicts: an answer says the event
        happens within the horizon after it was made, and an outcome labels
        the prediction window its `observed_at` falls in. A whole number and a
        unit (`s`, `m`, `h` or `d`); `0s` for labelled examples, which join
        the answer current at `observed_at`.
      pattern: ^(0|[1-9][0-9]*)[smhd]$
      default: 0s
    AttributeFilter:
      type: object
      description: >
        Which documents something applies to, by their attributes. Each key is
        an attribute path; a value is equality, and a

        list of values is `In`, as in query filters. Keys combine with `And`.
      minProperties: 1
      maxProperties: 8
      propertyNames:
        type: string
        pattern: ^attributes\.[A-Za-z0-9_-]+$
      additionalProperties:
        oneOf:
          - $ref: '#/components/schemas/AttributeFilterValue'
          - type: array
            minItems: 1
            items:
              $ref: '#/components/schemas/AttributeFilterValue'
    RelationField:
      type: string
      description: >-
        A path in a related document, `id`, `created_at` or `updated_at`; or a
        plain judgment's `answers.<j>.value` or `answers.<j>.thresholds.<name>`,
        which render as they are. 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_-]+))$
    OutcomeRuleCondition:
      type: object
      description: |
        The value at `path` becomes `becomes`, or one of `in`, in this write.
        It fires only when the value was not already the target (for `in`,
        not already in the list); numbers compare by value.
      additionalProperties: false
      required:
        - path
      oneOf:
        - required:
            - becomes
        - required:
            - in
      properties:
        path:
          $ref: '#/components/schemas/DocumentPath'
        becomes:
          type:
            - string
            - number
            - boolean
        in:
          type: array
          minItems: 1
          maxItems: 16
          items:
            type:
              - string
              - number
              - boolean
    DocumentPath:
      type: string
      description: >-
        One attribute, `attributes.<name>`, or a key path inside state,
        `state.<key>[.<key>...]`.
      pattern: ^(attributes\.[A-Za-z0-9_-]+|state(\.[^.]+)+)$
      examples:
        - attributes.status
        - state.team
    Relation:
      type: object
      description: |
        Documents that point at the judged document.
        They are ordered newest `created_at` first: `window` keeps those
        created within the window, then `last_n` keeps the newest 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.

        With `join: {theirs: "id", mine: "attributes.<name>"}`
        the relation reads the one document the judged document points at,
        its referenced document, and takes neither `last_n` nor `window`.

        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 plain
        judgments' answers (`answers.<j>.<field>`), banded or cut.
      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 neither `last_n`
            nor `window`.
          if:
            properties:
              join:
                type: object
                properties:
                  theirs:
                    const: id
          then:
            not:
              anyOf:
                - required:
                    - last_n
                - required:
                    - window
        - 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
      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 newest n. Not on a relation that reads a referenced
            document; 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`,
            newest first. An entry may be a `BandedField`, which renders a
            number as its band. An entry may also be a plain 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'
        aggregate:
          $ref: '#/components/schemas/Aggregate'
    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.
          minItems: 1
          items:
            type: string
            pattern: ^(state|attributes)(\.[^.]+)+$
        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_-]+$
    AttributeFilterValue:
      type:
        - string
        - number
        - boolean
    RelationMatch:
      type: object
      description: >
        Which documents a relation reads: an `AttributeFilter`, and also a plain
        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}`. 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, 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_-]+))$
      additionalProperties:
        oneOf:
          - $ref: '#/components/schemas/AttributeFilterValue'
          - type: array
            minItems: 1
            items:
              $ref: '#/components/schemas/AttributeFilterValue'
          - $ref: '#/components/schemas/AnswerCut'
    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. Two different

        attributes are `invalid_request`.
      oneOf:
        - $ref: '#/components/schemas/ReferringJoin'
        - $ref: '#/components/schemas/ReferencedJoin'
        - $ref: '#/components/schemas/BlockingJoin'
    BandedField:
      type: object
      description: >
        A number rendered as its band rather than its value, in any relation. 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 plain 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
    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`, `min` and `max` skip values that are not
        numbers; `sum` of nothing is 0, and `min` and `max` of nothing are
        null. `latest` is the value in the newest document that has one, of
        any JSON type.

        `count_where` counts documents by plain 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`
        over an answer is `invalid_request`: it would move on every child
        evaluation.
      additionalProperties: false
      minProperties: 1
      properties:
        count:
          const: true
          description: How many documents the relation selected.
        count_where:
          $ref: '#/components/schemas/CountWhere'
        sum:
          $ref: '#/components/schemas/AggregatePaths'
        min:
          $ref: '#/components/schemas/ExtremePaths'
        max:
          $ref: '#/components/schemas/ExtremePaths'
        latest:
          $ref: '#/components/schemas/LatestPaths'
    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
    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_-]+$
    CountWhere:
      type: object
      description: |
        How many selected documents match every key: a
        plain 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. 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_-]+)$
      additionalProperties:
        oneOf:
          - $ref: '#/components/schemas/AttributeFilterValue'
          - type: array
            minItems: 1
            items:
              $ref: '#/components/schemas/AttributeFilterValue'
          - $ref: '#/components/schemas/AnswerCut'
    AggregatePaths:
      type: array
      minItems: 1
      maxItems: 8
      uniqueItems: true
      items:
        type: string
        description: A `state` or `attributes` path. Keys in it cannot contain `(` or `)`.
        pattern: ^(state|attributes)(\.[^.()]+)+$
    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 plain judgment's `answers.<j>.value` or
        `answers.<j>.thresholds.<name>`.
      minItems: 1
      maxItems: 8
      uniqueItems: true
      items:
        type: string
        description: >-
          A `state` or `attributes` path, or an answer's `value` or
          `thresholds.<name>`. Keys in it cannot contain `(` or `)`.
        pattern: >-
          ^((state|attributes)(\.[^.()]+)+|answers\.[A-Za-z0-9_-]+\.(value|thresholds\.[A-Za-z0-9_-]+))$
  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
  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'
    PaymentRequired:
      description: >-
        `budget_exceeded`: the namespace is over its compute budget for the
        billing period, or the estimate does not fit it. Or `plan_required`: the
        organization's plan does not include this; `details` names the
        `capability`, the cheapest `required_plan` that includes it and the
        organization's `plan`.
      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'
    CreateJudgmentConflict:
      description: >-
        `conflict`: as for any route (the namespace is being deleted, or the
        judgment is inherited from a template), or concurrent creates of the
        same name kept this one from a version number. Nothing was created;
        retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooLarge:
      description: >-
        `too_large`: the request body (on any route), a document, or the
        estimated query scan is over its limit. `details` carries the estimate
        for queries. It also answers a write that would take
        `default/quickstart` past its size cap (10,000 documents or 100 MB),
        with `details.max_documents` and `details.max_bytes`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    EngineVersionUnavailable:
      description: >-
        `engine_version_unavailable`: the engine version is not active in the
        registry.
      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'
    CreateJudgmentUnavailable:
      description: >-
        `engine_unavailable`: the engine registry is not available yet; or
        `unavailable`: the version could not be saved just now. Nothing was
        created. Retry after `Retry-After`.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      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

- [Judgments](/concepts/judgments.md)
- [Composite judgments](/guides/composite-judgments.md)
- [Import existing data](/guides/import-existing-data.md)
- [Create a subscription](/api-reference/subscriptions/create-a-subscription.md)
- [Measure, improve, tune](/guides/measure-improve-tune.md)


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