> ## 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 evaluations or items

> Say what the right answer was: the one way a label becomes evidence. Send one submission, or many as `submissions` (up to 200, each exactly as one call would send it): every submission is checked first, then all are stored in one transaction, or none are (a refusal's `param` starts `submissions.<i>`); the response then lists each one's result in `results`. 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 imported examples; `app_native` and `app_resolution` provide supporting feedback only. Use `source: "owner"` for your own judgment as the decision's owner: its expected_outcome or verdict claim decides the consensus; expected_answer 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 end-level 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, their reasons can reach the suggestion generator, and their `input_broken` claims set the evaluation aside. See `source`. Records claims about an evaluation in the key's environment, or about an item's latest primary evaluation, at two levels: `expected_answer` (what one question's answer should have been) and `expected_outcome` or `verdict` (what the decision should have produced, or approve/reject of the outcome it returned), plus `input_broken`. Claims are checked against that evaluation (400 with `param`): an expected_answer names a question of its structure (`claims.<i>.question`), one per question; an expected_outcome is one of the outcomes the result can produce (`claims.<i>.value`); at most one end-level claim, never verdict and expected_outcome together; a result that is a value (a number) takes no end-level claim; 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 an item's state against a decision structure
    (System One questions, attributes you supply and aggregations over both),
    record feedback and import labeled examples, review a ranked queue, and
    improve the structure 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/structures/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/structures/hash` and `POST /v1/review/items/fill`,
    whose retry is another fill within the day's budget). 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 evaluations or items
      description: >-
        Say what the right answer was: the one way a label becomes evidence.
        Send one submission, or many as `submissions` (up to 200, each exactly
        as one call would send it): every submission is checked first, then all
        are stored in one transaction, or none are (a refusal's `param` starts
        `submissions.<i>`); the response then lists each one's result in
        `results`. 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 imported examples; `app_native` and `app_resolution` provide
        supporting feedback only. Use `source: "owner"` for your own judgment as
        the decision's owner: its expected_outcome or verdict claim decides the
        consensus; expected_answer 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 end-level 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,
        their reasons can reach the suggestion generator, and their
        `input_broken` claims set the evaluation aside. See `source`. Records
        claims about an evaluation in the key's environment, or about an item's
        latest primary evaluation, at two levels: `expected_answer` (what one
        question's answer should have been) and `expected_outcome` or `verdict`
        (what the decision should have produced, or approve/reject of the
        outcome it returned), plus `input_broken`. Claims are checked against
        that evaluation (400 with `param`): an expected_answer names a question
        of its structure (`claims.<i>.question`), one per question; an
        expected_outcome is one of the outcomes the result can produce
        (`claims.<i>.value`); at most one end-level claim, never verdict and
        expected_outcome together; a result that is a value (a number) takes no
        end-level claim; 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 decision should have produced another
                  outcome
                value:
                  about:
                    evaluation_id: evl_01900000-0000-7000-8000-000000000000
                  source: owner
                  claims:
                    - kind: expected_outcome
                      value: review
                  reason: >-
                    The seller is verified, so this needs a person, not a
                    removal.
              owner_answer:
                summary: >-
                  As the owner: the right outcome, and the question that was
                  answered wrongly
                value:
                  about:
                    evaluation_id: evl_01900000-0000-7000-8000-000000000000
                  source: owner
                  claims:
                    - kind: expected_outcome
                      value: allow
                    - kind: expected_answer
                      question: 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/FeedbackBody'
      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:
                anyOf:
                  - $ref: '#/components/schemas/FeedbackResponse'
                  - $ref: '#/components/schemas/FeedbackBatchResponse'
        '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'
        '413':
          description: The HTTP body exceeds 1 MB (reason BODY_TOO_LARGE)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: >-
            The request is well formed but cannot be processed
            (IDEMPOTENCY_KEY_REUSED requires a fresh key; REQUEST_TOO_LARGE
            requires a smaller request)
          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)
          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:
    FeedbackBody:
      anyOf:
        - $ref: '#/components/schemas/Feedback'
        - $ref: '#/components/schemas/FeedbackBatch'
    FeedbackResponse:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        id:
          $ref: '#/components/schemas/FbkIdOutput'
        evaluation_id:
          $ref: '#/components/schemas/EvlIdOutput'
        item_id:
          type: string
        claim_count:
          type: integer
          minimum: 1
        resolved_from_item:
          type: boolean
      required:
        - environment
        - id
        - evaluation_id
        - item_id
        - claim_count
        - resolved_from_item
      additionalProperties: true
      x-moonbounce-sdk-known-fields: true
    FeedbackBatchResponse:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        results:
          type: array
          items:
            $ref: '#/components/schemas/FeedbackResponse'
          description: One per submission, in order.
      required:
        - environment
        - results
      additionalProperties: true
      x-moonbounce-sdk-known-fields: true
    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
                (`attribute_values.region`, `claims.1.value`).
            details:
              type: object
              properties:
                issues:
                  type: array
                  items:
                    $ref: '#/components/schemas/ErrorIssue'
              additionalProperties: {}
          required:
            - type
            - message
          additionalProperties: true
          x-moonbounce-sdk-known-fields: true
      required:
        - error
      additionalProperties: true
      x-moonbounce-sdk-known-fields: true
    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:
            expected_answer (at most one per question), expected_outcome or
            verdict (at most one, and not both), and input_broken.
        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: >-
            Optional: why the answer was off, in your words. It is context for
            whoever writes the next change to the decision, so the most useful
            reason names the question that got it wrong, quotes the words in the
            content that show it, and states the rule that applies. A sentence
            is enough.
        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
    FeedbackBatch:
      type: object
      properties:
        submissions:
          type: array
          items:
            $ref: '#/components/schemas/Feedback'
          minItems: 1
          maxItems: 200
          description: >-
            1 to 200 submissions, each exactly as one call would send it. All
            are checked first; then all are stored together, or none.
      required:
        - submissions
      additionalProperties: false
    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.
    FbkIdOutput:
      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}$
    EvlIdOutput:
      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
    ErrorIssue:
      type: object
      properties:
        param:
          type: string
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]+$
          description: >-
            Stable, extensible issue code; unknown codes still have a parameter
            and human-readable message.
        message:
          type: string
      required:
        - param
        - code
        - message
      additionalProperties: true
      x-moonbounce-sdk-known-fields: true
    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:
                - expected_answer
            question:
              type: string
              minLength: 1
              maxLength: 64
              description: A question of the evaluation's structure.
            value:
              type: boolean
              description: >-
                What the question's answer should have been: true or false for a
                noul question.
          required:
            - kind
            - question
            - value
          additionalProperties: false
          description: >-
            What one question's answer should have been. It is stored against
            the question as it was asked (its hash), so a reworded question does
            not inherit it. Aggregations and attributes take none: aggregations
            are computed and attributes are facts you supplied.
        - type: object
          properties:
            kind:
              type: string
              enum:
                - expected_outcome
            value:
              type: string
              minLength: 1
              maxLength: 200
              description: One of the outcomes the decision's result can produce.
          required:
            - kind
            - value
          additionalProperties: false
          description: >-
            The outcome the decision should have produced. A decision whose
            result is a value (a number) has no outcome and takes no
            expected_outcome or verdict.
        - 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 (status `decided`). An
            evaluation that abstained has only a fallback outcome, whatever it
            is called. Not with expected_outcome in one submission.
        - 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.
    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 example evaluations whose content is stored): `owner` through this
        route, reviewers answering through the review routes (the Review app),
        and imported examples. The owner outranks reviewers, and reviewers
        outrank imported examples. `app_native` and `app_resolution` provide
        supporting feedback only. `owner`: the decision owner's judgment. An
        expected_outcome or verdict claim decides the consensus; expected_answer
        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 end-level 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 other than an import, puts the group in
        training (an imported example's group is in the `example` split
        instead). 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.
    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.