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

# Submit feedback on an evaluation or item

> Say what the right answer was. The sources that count toward an evaluation's consensus, which previews and suggestions learn from, are `owner` (through this route), reviewers answering through the review routes (the Review app), and seed examples; `app_native` and `app_resolution` provide supporting feedback only. Use `source: "owner"` for your own judgment as the decision's owner: its outcome, target or verdict claim decides the consensus; feature claims alone decide nothing. All owner feedback through this route is the decision owner's single voice, whoever sent it (the latest owner submission with an outcome, target or verdict claim decides, replacing earlier ones); agreement between people is only kept for reviewers on the review routes. Use `app_native` or `app_resolution` for your app's own feedback (a user approved or reverted the outcome, an appeal settled it): they never decide a consensus, but the first feedback on an item's group, from any source, fixes its split for good (native feedback sent before your review keeps that item out of the holdout), their reasons can reach the suggestion generator, and their `input_broken` claims set the evaluation aside. See `source`. Records claims (target, outcome, verdict, feature, input_broken, action) about an evaluation in the key's environment, or about an item's latest primary evaluation. Claims are checked against that evaluation (400 with `param`): at most one outcome, target and verdict claim and one claim per feature, an outcome and target that agree, and verdicts only on primary evaluations that decided an outcome (shown, and not abstained). `supersedes` must name a submission by the same actor on the same evaluation that nothing has superseded yet (409). `owner` feedback cannot name an `actor` (403). Owner feedback, and superseding an owner submission, need the `decisions:develop` scope (403 INSUFFICIENT_SCOPE); integration sources need `decisions:feedback`. Read a submission back with `GET /v1/feedback/{feedback_id}`. A replay with the same Idempotency-Key returns the first result with the Idempotent-Replayed header.



## OpenAPI

````yaml /openapi.json post /v1/feedback
openapi: 3.1.0
info:
  title: Moonbounce Decisions API
  version: 0.0.0
  description: >-
    Moonbounce decisions: evaluate items against a decision spec (features
    combined by aggregators), record feedback and seed examples, review a ranked
    queue, and improve the spec through suggestions that ship as code diffs,
    each with a preview (backtest) and an evidence label.


    **Authentication.** Send an API key as `Authorization: Bearer <key>` on
    every operation except `GET /health` and `POST /v1/specs/hash`. Call the API
    from a server, never a browser: a key must not reach one, and the API sends
    no CORS headers. A key belongs to one organization and one environment:
    production, or test for a key with the `env:test` permission. Test traffic
    never counts as production traffic.


    **Requests.** JSON bodies (`Content-Type: application/json`) of at most 1
    MB, parsed strictly: duplicate keys, lone surrogates, integers outside
    ±(2^53 - 1), `__proto__` keys, U+0000 and nesting deeper than 64 are a 400.


    **Retries.** Send an `Idempotency-Key` on every POST under /v1 that takes
    one (all but `POST /v1/specs/hash`). Keep it for the request's retries. The
    same key with the same request returns the first stored result, marked
    `Idempotent-Replayed: true`, for 24 hours (410 if that result was since
    deleted); with another request it is 422 IDEMPOTENCY_KEY_REUSED. Keys are
    scoped to the organization and environment across every operation, so use a
    fresh key per request. A request that failed before its result was stored
    runs again when retried. 429 and 503 under /v1 may carry `Retry-After`
    (seconds); a daily QUOTA_EXCEEDED cap instead names its reset time.


    **Errors.** Every error under /v1 is `{"error": {"type", "message",
    "reason"?, "param"?, "details"?}}` (ErrorBody); `GET /health` answers its
    own shape. Every response carries a `Request-Id` header. Rate limits apply
    per key, and per client IP for requests without a valid key; a 429
    RATE_LIMITED carries `Retry-After`.
servers:
  - url: https://api.moonbounce.ai
    description: Production
security: []
paths:
  /v1/feedback:
    post:
      tags:
        - feedback
      summary: Submit feedback on an evaluation or item
      description: >-
        Say what the right answer was. The sources that count toward an
        evaluation's consensus, which previews and suggestions learn from, are
        `owner` (through this route), reviewers answering through the review
        routes (the Review app), and seed examples; `app_native` and
        `app_resolution` provide supporting feedback only. Use `source: "owner"`
        for your own judgment as the decision's owner: its outcome, target or
        verdict claim decides the consensus; feature claims alone decide
        nothing. All owner feedback through this route is the decision owner's
        single voice, whoever sent it (the latest owner submission with an
        outcome, target or verdict claim decides, replacing earlier ones);
        agreement between people is only kept for reviewers on the review
        routes. Use `app_native` or `app_resolution` for your app's own feedback
        (a user approved or reverted the outcome, an appeal settled it): they
        never decide a consensus, but the first feedback on an item's group,
        from any source, fixes its split for good (native feedback sent before
        your review keeps that item out of the holdout), their reasons can reach
        the suggestion generator, and their `input_broken` claims set the
        evaluation aside. See `source`. Records claims (target, outcome,
        verdict, feature, input_broken, action) about an evaluation in the key's
        environment, or about an item's latest primary evaluation. Claims are
        checked against that evaluation (400 with `param`): at most one outcome,
        target and verdict claim and one claim per feature, an outcome and
        target that agree, and verdicts only on primary evaluations that decided
        an outcome (shown, and not abstained). `supersedes` must name a
        submission by the same actor on the same evaluation that nothing has
        superseded yet (409). `owner` feedback cannot name an `actor` (403).
        Owner feedback, and superseding an owner submission, need the
        `decisions:develop` scope (403 INSUFFICIENT_SCOPE); integration sources
        need `decisions:feedback`. Read a submission back with `GET
        /v1/feedback/{feedback_id}`. A replay with the same Idempotency-Key
        returns the first result with the Idempotent-Replayed header.
      operationId: submitFeedback
      parameters:
        - schema:
            type: string
            minLength: 1
            maxLength: 255
            pattern: ^[\x20-\x7e]+$
            description: >-
              Makes a retry safe. For 24 hours, the same key with the same
              request returns the first stored result (with Idempotent-Replayed:
              true); the same key with a different request is a 422
              IDEMPOTENCY_KEY_REUSED. Keys are scoped to the organization and
              environment across every operation, so use a fresh key per
              request. A request that failed before its result was stored runs
              again when retried with its key.
          required: false
          description: >-
            Makes a retry safe. For 24 hours, the same key with the same request
            returns the first stored result (with Idempotent-Replayed: true);
            the same key with a different request is a 422
            IDEMPOTENCY_KEY_REUSED. Keys are scoped to the organization and
            environment across every operation, so use a fresh key per request.
            A request that failed before its result was stored runs again when
            retried with its key.
          name: idempotency-key
          in: header
      requestBody:
        required: true
        content:
          application/json:
            examples:
              owner_outcome:
                summary: 'As the owner: the right outcome was different'
                value:
                  about:
                    evaluation_id: evl_01900000-0000-7000-8000-000000000000
                  source: owner
                  claims:
                    - kind: outcome
                      value: review
                  reason: >-
                    The seller is verified, so this needs a person, not a
                    rejection.
              owner_feature:
                summary: >-
                  As the owner: the right outcome, and the feature that was
                  answered wrongly
                value:
                  about:
                    evaluation_id: evl_01900000-0000-7000-8000-000000000000
                  source: owner
                  claims:
                    - kind: outcome
                      value: approve
                    - kind: feature
                      feature: counterfeit
                      value: false
              native_approve:
                summary: 'From your app: a user approved the outcome'
                value:
                  about:
                    evaluation_id: evl_01900000-0000-7000-8000-000000000000
                  source: app_native
                  claims:
                    - kind: verdict
                      value: approve
            schema:
              $ref: '#/components/schemas/Feedback'
      responses:
        '201':
          description: Recorded
          headers:
            Idempotent-Replayed:
              schema:
                type: string
                enum:
                  - 'true'
                description: >-
                  Present and `true` when this is the stored result of an
                  earlier request with the same Idempotency-Key.
              required: false
              description: >-
                Present and `true` when this is the stored result of an earlier
                request with the same Idempotency-Key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackResponse'
        '400':
          description: 'Invalid request: `param` names the field at fault when one applies'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '401':
          description: Missing or invalid API key (reason KEY_INVALID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: The key may not do this
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Not found, or not in this key's organization and environment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '409':
          description: Conflict with the current state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '410':
          description: >-
            The Idempotency-Key's first result was deleted, so it cannot be
            replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: >-
            The request is well formed but cannot be processed (for example
            reason IDEMPOTENCY_KEY_REUSED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '429':
          description: >-
            RATE_LIMITED: retry after Retry-After seconds; QUOTA_EXCEEDED:
            details.reset_at gives the daily reset time (no Retry-After), or
            details.too_large asks for a smaller request
          headers:
            Retry-After:
              schema:
                type: string
                description: Seconds to wait before retrying, when waiting helps.
              required: false
              description: Seconds to wait before retrying, when waiting helps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '503':
          description: >-
            Temporarily unavailable, with Retry-After (for example reason
            AUTH_UNAVAILABLE, ENGINE_UNAVAILABLE or STORE_UNAVAILABLE): retry
            with the same Idempotency-Key
          headers:
            Retry-After:
              schema:
                type: string
                description: Seconds to wait before retrying, when waiting helps.
              required: true
              description: Seconds to wait before retrying, when waiting helps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
        - bearerAuth:
            - decisions:feedback
        - bearerAuth:
            - decisions:develop
components:
  schemas:
    Feedback:
      type: object
      properties:
        about:
          $ref: '#/components/schemas/FeedbackAbout'
        claims:
          type: array
          items:
            $ref: '#/components/schemas/Claim'
          minItems: 1
          maxItems: 65
          description: >-
            What you are saying about the evaluation, 1 to 65 claims. At most
            one outcome, one target and one verdict claim, and one claim per
            feature; an outcome and a target must not contradict each other.
        source:
          $ref: '#/components/schemas/PublicFeedbackSource'
        actor:
          type: string
          minLength: 1
          maxLength: 256
          description: >-
            Who in your system this comes from, for example a user or agent id.
            It is namespaced under your key. Not allowed with source `owner`:
            the owner is you.
        reason:
          type: string
          maxLength: 4000
          description: Why, in your words. Optional.
        unsure:
          type: boolean
          description: True when the judge is not confident in these claims.
        supersedes:
          type: string
          pattern: >-
            ^fbk_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
          description: >-
            The id of your own earlier submission on the same evaluation that
            this one replaces. Only the latest submission in a chain counts; the
            earlier one stays readable and shows `superseded_by`.
      required:
        - about
        - claims
        - source
      additionalProperties: false
    FeedbackResponse:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        id:
          $ref: '#/components/schemas/FbkId'
        evaluation_id:
          $ref: '#/components/schemas/EvlId'
        item_id:
          type: string
        claims:
          type: integer
          minimum: 1
        resolved_from_item:
          type: boolean
      required:
        - environment
        - id
        - evaluation_id
        - item_id
        - claims
        - resolved_from_item
      additionalProperties: false
    ErrorBody:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              $ref: '#/components/schemas/ErrorType'
            reason:
              type: string
              pattern: ^[A-Z][A-Z0-9_]+$
              description: >-
                Set where a caller can act on it, one of the reasons in the
                ErrorReason schema. New reasons may be added: treat an unknown
                one by the status and `type`.
            message:
              type: string
              description: For people; don't parse it.
            param:
              type: string
              description: >-
                The request field at fault, as a dotted path (`subject.0.text`,
                `claims.1.value`).
            details:
              type: object
              additionalProperties: {}
          required:
            - type
            - message
          additionalProperties: false
      required:
        - error
      additionalProperties: false
    FeedbackAbout:
      anyOf:
        - $ref: '#/components/schemas/FeedbackAboutEvaluation'
        - $ref: '#/components/schemas/FeedbackAboutItem'
      description: >-
        What the feedback is about. `{evaluation_id}` is preferred: it names
        exactly the answer you are judging. `{decision_id, item_id}` means the
        item's latest primary evaluation in the key's environment, resolved when
        the feedback arrives; the response says which one. Verdict claims need
        `evaluation_id`.
    Claim:
      oneOf:
        - type: object
          properties:
            kind:
              type: string
              enum:
                - target
            value:
              type: boolean
              description: Whether the spec's target statement is true of this item.
          required:
            - kind
            - value
          additionalProperties: false
          description: >-
            Whether the target statement is true. Only for specs that declare a
            target.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - outcome
            value:
              type: string
              minLength: 1
              maxLength: 64
              description: >-
                The right outcome: one of this evaluation's outcomes (`below` or
                a cut's outcome); an `abstain` name counts only where it reuses
                one of them.
          required:
            - kind
            - value
          additionalProperties: false
          description: >-
            The outcome this item should have had. On a cut score, the lowest
            outcome also says the target is false and the highest says it is
            true; middle outcomes say nothing about the target.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - verdict
            value:
              type: string
              enum:
                - approve
                - reject
              description: 'approve: the shown outcome was right. reject: it was wrong.'
          required:
            - kind
            - value
          additionalProperties: false
          description: >-
            Approve or reject the outcome that was shown. Needs
            `about.evaluation_id`, and an evaluation whose outcome was shown to
            someone: primary traffic that decided (it has a score). An
            evaluation that abstained has only a fallback outcome, whatever it
            is called.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - feature
            feature:
              type: string
              minLength: 1
              maxLength: 64
              description: A boolean feature of the evaluation's spec.
            value:
              type: boolean
              description: What the feature's answer should have been.
          required:
            - kind
            - feature
            - value
          additionalProperties: false
          description: >-
            The expected answer of one boolean feature. It is stored against
            that feature's prompt as it was judged, so a reworded prompt does
            not inherit it.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - input_broken
            problem:
              type: string
              enum:
                - truncated
                - wrong_language
                - bad_extraction
                - unreadable
                - other
          required:
            - kind
            - problem
          additionalProperties: false
          description: >-
            The content could not be judged. The evaluation is kept out of fits
            and previews.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - action
            value:
              type: string
              minLength: 1
              maxLength: 128
              description: >-
                What your app did, in your own words (for example
                `auto_approved`).
            logic_version:
              type: string
              maxLength: 128
              description: Which version of your own logic acted.
          required:
            - kind
            - value
          additionalProperties: false
          description: >-
            What your app did with the outcome. Observed behavior, not a
            statement of what is true.
    PublicFeedbackSource:
      type: string
      enum:
        - app_native
        - app_resolution
        - owner
      description: >-
        Who is speaking. Three sources count toward an evaluation's consensus
        answer, which is what previews and suggestions learn from (on primary
        and seed evaluations whose content is stored): `owner` through this
        route, reviewers answering through the review routes (the Review app),
        and seed examples. The owner outranks reviewers, and reviewers outrank
        seeds. `app_native` and `app_resolution` provide supporting feedback
        only. `owner`: the decision owner's judgment. An outcome, target or
        verdict claim decides the consensus; feature claims refine a case that
        has a consensus and on their own decide nothing. `owner` folds every
        person into one: all owner feedback through this route is the decision
        owner's single voice, whoever sent it, and the latest owner submission
        with an outcome, target or verdict claim decides, replacing earlier
        ones. Agreement between people is only kept for reviewers on the review
        routes, who each get one vote. Use `owner` for corrections you stand
        behind. `app_native`: your app's own feedback about the outcome it acted
        on, for example a user approved or reverted it. `app_resolution`: how
        your own process finally settled the case, for example an appeal. The
        two weak sources never decide a consensus, but they are not inert. The
        first feedback a group of near-identical items gets in an environment,
        from any source, fixes the group's split for good: an owner's or
        reviewer's first feedback holds out 30% of groups to test suggestions,
        while a native or resolution first touch puts the group in training, so
        a native verdict sent before your own review means that item can never
        be held out. A native or resolution submission's reason is shown to the
        suggestion generator alongside a case that other feedback decided. An
        `input_broken` claim from any source sets the evaluation aside.
    Environment:
      type: string
      enum:
        - production
        - test
      description: >-
        The environment of the key that made the call: `production` or `test`.
        Evaluations, feedback, reviews and suggestions are separate per
        environment; registered structures are shared.
    FbkId:
      type: string
      pattern: >-
        ^fbk_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
    EvlId:
      type: string
      pattern: >-
        ^evl_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
    ErrorType:
      type: string
      enum:
        - invalid_request
        - authentication
        - permission_denied
        - not_found
        - conflict
        - rate_limited
        - engine_failed
        - unavailable
        - internal
    FeedbackAboutEvaluation:
      type: object
      properties:
        evaluation_id:
          type: string
          pattern: >-
            ^evl_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
          description: The evaluation this judges. Required for verdict claims.
      required:
        - evaluation_id
      additionalProperties: false
    FeedbackAboutItem:
      type: object
      properties:
        decision_id:
          $ref: '#/components/schemas/DecisionId'
        item_id:
          type: string
          minLength: 1
          maxLength: 256
          description: The item id you sent to evaluate.
      required:
        - decision_id
        - item_id
      additionalProperties: false
    DecisionId:
      type: string
      pattern: ^[a-z][a-z0-9_.-]{0,127}$
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        An API key, sent as `Authorization: Bearer <key>`. Each operation lists
        the scope its key needs: `decisions:evaluate` (evaluate and read
        evaluations), `decisions:feedback` (your app's feedback) or
        `decisions:develop` (everything else, including owner feedback and the
        review queue). An operation listing no scope takes any scoped key. A key
        with no scope, or without the operation's, gets 403 INSUFFICIENT_SCOPE.

````

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