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

# Export a decision's feedback as training data

> Lists feedback with its claims and evaluated cases; reads only. The raw feedback on a decision, for a producer's training data (calibration, weights, suggested versions): one item per submission, oldest first, with its claims, the evaluation it judged (version hashes, engine profile, cut points, score, outcome, each leaf's answer and distribution, and the content when the decision stores it) and its group's split. Holdout feedback is never listed, not its claims, its evaluation or its content; it is only counted in `withheld.holdout`, together with feedback whose content's text belongs to a held-out group, so suggestions can still be tested on feedback their producer never saw. `withheld` counts submission rows, including withdrawal events, not distinct evaluations. It is counted on the first page only (no `cursor`) and is null on later pages. Counts per `structure` or `weights` filter can tell you that a version gained held-out feedback, never which item. It is raw, not a consensus: superseded submissions are listed and marked `superseded_by`, and how submissions become training data is the reader's call. `withdrawn_at` marks a claimless withdrawal event: resolve chain heads across pages, exclude withdrawn chains without reviving ancestors, and invalidate cached answers. On a spot check, `after_reveal` is false on the reviewer's first, blind answer and true on each answer that changed it after Moonbounce's call was revealed: pick the blind answer to measure accuracy, the latest to train. `channel` says where each submission came from; Slack's terms bar training an LLM on `slack` feedback. Scoped to the key's organization and environment, and feedback on deleted evaluations is left out. A decision with no feedback this key can see answers 200 with no items. `structure` and `weights` keep only evaluations on those hashes. `split.group` is an opaque id shared by submissions on near-identical items in the key's environment; it never names an item. Page forward by passing `next_cursor` as `cursor` while `has_more` is true. `next_cursor` is also set on the last page, so keep it: reading from it later returns what was written after it, apart from the following. Items are in creation order, not commit order, and a submission's creation time is the start of the transaction that wrote it: any write that commits after a later one was read (a seed batch, or any concurrent submission) can land behind a cursor already handed out, and a holdout group that is burned later appears under its submissions' original creation time. A reason added later to a submission already read (a reviewer's `POST /v1/review/items/{item_id}/reason`) does not move it either. So a forward reader re-reads from the start now and then. Not an MCP tool, so bulk content stays out of coding agents' context. The web session may not read it (403).



## OpenAPI

````yaml /openapi.json get /v1/decisions/{decision_id}/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, counted per Cloudflare location.
servers:
  - url: https://api.moonbounce.ai
    description: Production
security: []
paths:
  /v1/decisions/{decision_id}/feedback:
    get:
      tags:
        - feedback
      summary: Export a decision's feedback as training data
      description: >-
        Lists feedback with its claims and evaluated cases; reads only. The raw
        feedback on a decision, for a producer's training data (calibration,
        weights, suggested versions): one item per submission, oldest first,
        with its claims, the evaluation it judged (version hashes, engine
        profile, cut points, score, outcome, each leaf's answer and
        distribution, and the content when the decision stores it) and its
        group's split. Holdout feedback is never listed, not its claims, its
        evaluation or its content; it is only counted in `withheld.holdout`,
        together with feedback whose content's text belongs to a held-out group,
        so suggestions can still be tested on feedback their producer never saw.
        `withheld` counts submission rows, including withdrawal events, not
        distinct evaluations. It is counted on the first page only (no `cursor`)
        and is null on later pages. Counts per `structure` or `weights` filter
        can tell you that a version gained held-out feedback, never which item.
        It is raw, not a consensus: superseded submissions are listed and marked
        `superseded_by`, and how submissions become training data is the
        reader's call. `withdrawn_at` marks a claimless withdrawal event:
        resolve chain heads across pages, exclude withdrawn chains without
        reviving ancestors, and invalidate cached answers. On a spot check,
        `after_reveal` is false on the reviewer's first, blind answer and true
        on each answer that changed it after Moonbounce's call was revealed:
        pick the blind answer to measure accuracy, the latest to train.
        `channel` says where each submission came from; Slack's terms bar
        training an LLM on `slack` feedback. Scoped to the key's organization
        and environment, and feedback on deleted evaluations is left out. A
        decision with no feedback this key can see answers 200 with no items.
        `structure` and `weights` keep only evaluations on those hashes.
        `split.group` is an opaque id shared by submissions on near-identical
        items in the key's environment; it never names an item. Page forward by
        passing `next_cursor` as `cursor` while `has_more` is true.
        `next_cursor` is also set on the last page, so keep it: reading from it
        later returns what was written after it, apart from the following. Items
        are in creation order, not commit order, and a submission's creation
        time is the start of the transaction that wrote it: any write that
        commits after a later one was read (a seed batch, or any concurrent
        submission) can land behind a cursor already handed out, and a holdout
        group that is burned later appears under its submissions' original
        creation time. A reason added later to a submission already read (a
        reviewer's `POST /v1/review/items/{item_id}/reason`) does not move it
        either. So a forward reader re-reads from the start now and then. Not an
        MCP tool, so bulk content stays out of coding agents' context. The web
        session may not read it (403).
      operationId: listFeedback
      parameters:
        - schema:
            $ref: '#/components/schemas/DecisionId'
          required: true
          name: decision_id
          in: path
        - schema:
            type: string
            pattern: ^sha256:[0-9a-f]{64}$
            description: Only feedback on evaluations of this structure hash.
          required: false
          description: Only feedback on evaluations of this structure hash.
          name: structure
          in: query
        - schema:
            type: string
            pattern: ^sha256:[0-9a-f]{64}$
            description: Only feedback on evaluations with this weights hash.
          required: false
          description: Only feedback on evaluations with this weights hash.
          name: weights
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          required: false
          name: limit
          in: query
        - schema:
            type: string
            minLength: 1
            maxLength: 1024
            description: The previous page's `next_cursor`.
          required: false
          description: The previous page's `next_cursor`.
          name: cursor
          in: query
      responses:
        '200':
          description: A page of feedback, oldest first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackExport'
        '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'
        '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:develop
components:
  schemas:
    DecisionId:
      type: string
      pattern: ^[a-z][a-z0-9_.-]{0,127}$
    FeedbackExport:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        items:
          type: array
          items:
            $ref: '#/components/schemas/FeedbackExportItem'
        next_cursor:
          type:
            - string
            - 'null'
          description: >-
            Where to resume: pass it as `cursor`. Set whenever the page has
            items (the last item's position) and, on an empty page read with a
            cursor, that cursor again, so a reader at the end keeps its place.
            Reading from it later returns what was written after it, apart from
            writes that commit behind it and reasons added later to submissions
            already read (see the endpoint), so re-read from the start now and
            then. Null only on an empty first page.
        has_more:
          type: boolean
          description: True when more items follow this page now.
        withheld:
          anyOf:
            - type: object
              properties:
                holdout:
                  type: integer
                  minimum: 0
                  description: >-
                    Submissions in held-out groups, and submissions whose
                    content's text belongs to a held-out group: counted, never
                    listed.
                unassigned:
                  type: integer
                  minimum: 0
                  description: Submissions whose item has no split yet.
              required:
                - holdout
                - unassigned
              additionalProperties: false
            - type: 'null'
          description: >-
            Submission rows, including withdrawal events (not distinct
            evaluations), in scope (this decision, the key's environment, the
            same version filters) that the export leaves out of every page.
            Counted on the first page only (no `cursor`); null on later pages.
            Counts per filter can tell you that a version gained held-out
            feedback, never which item.
      required:
        - environment
        - items
        - next_cursor
        - has_more
        - withheld
      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
    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.
    FeedbackExportItem:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/FbkId'
        source:
          $ref: '#/components/schemas/FeedbackSource'
        channel:
          type: string
          enum:
            - slack
            - web
            - api
            - mcp
            - import
          description: >-
            The surface the submission came through, stamped by the server.
            Slack's terms bar training an LLM on Slack data: leave out `slack`
            when you fit a model.
        actor:
          type: string
          description: >-
            Who submitted it, as recorded (an `ext:` prefix marks an actor a key
            named).
        reason:
          type: string
        reason_pending:
          type: boolean
          description: >-
            True when a reason was promised and has not arrived yet. On a
            superseded submission (`superseded_by` set) it means the reason was
            never added before the answer was replaced, and none will be: read
            the chain head.
        reason_added_at:
          type: string
          format: date-time
          description: >-
            When a pending reason was filled in. A reason added after the answer
            was revealed is commentary, not blind evidence.
        unsure:
          type: boolean
        withdrawn_at:
          type: string
          format: date-time
          description: >-
            When this claimless submission withdrew its chain. Resolve chain
            heads before excluding withdrawn chains; ancestors never revive.
        after_reveal:
          type: boolean
          description: >-
            Spot checks only. `false` on the reviewer's first answer, given
            blind; `true` on an answer that changed it after Moonbounce's call
            was revealed. Accuracy is measured on the blind answer. Omitted on
            other items, other sources and withdrawal events.
        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 submission this one replaces.
        superseded_by:
          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: >-
            Set when a later submission replaced this one. This one no longer
            counts.
        created_at:
          $ref: '#/components/schemas/Timestamp'
        claims:
          type: array
          items:
            $ref: '#/components/schemas/ExportedClaim'
          description: The claims, in the order they were sent.
        evaluation:
          $ref: '#/components/schemas/ExportedEvaluation'
        split:
          type: object
          properties:
            split:
              $ref: '#/components/schemas/ExportedSplit'
            group:
              type: string
              pattern: ^grp_[0-9a-f]{32}$
              description: >-
                An opaque, stable id of the item's group of near-identical items
                in the key's environment: submissions with the same `group`
                share one. It never names an item.
          required:
            - split
            - group
          additionalProperties: false
          description: >-
            The item's group and its side of the training line: `seed` (a
            starting example), `train`, or `burned` (a holdout group whose cases
            were shown in a preview, that was merged with a near-duplicate
            group, or whose content was later imported as a seed example; it
            counts as training).
      required:
        - id
        - source
        - channel
        - actor
        - reason_pending
        - unsure
        - created_at
        - claims
        - evaluation
        - split
      additionalProperties: false
    ErrorType:
      type: string
      enum:
        - invalid_request
        - authentication
        - permission_denied
        - not_found
        - conflict
        - rate_limited
        - engine_failed
        - unavailable
        - internal
    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}$
    FeedbackSource:
      type: string
      enum:
        - app_native
        - app_resolution
        - reviewer
        - owner
        - import
        - synthetic_review
    Timestamp:
      type: string
      format: date-time
    ExportedClaim:
      type: object
      properties:
        claim:
          $ref: '#/components/schemas/Claim'
        node_hash:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
          description: >-
            Feature claims only: the node hash of the prompt that was judged. A
            feature whose prompt has since changed has a different hash, and the
            claim does not apply to it.
        shown:
          type: object
          properties:
            structure:
              $ref: '#/components/schemas/VersionHash'
            weights:
              $ref: '#/components/schemas/VersionHash'
          required:
            - structure
            - weights
          additionalProperties: false
          description: The structure and weights whose answers the person saw.
      required:
        - claim
        - shown
      additionalProperties: false
    ExportedEvaluation:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/EvlId'
        item_id:
          type: string
        assignment_mode:
          $ref: '#/components/schemas/AssignmentMode'
        created_at:
          $ref: '#/components/schemas/Timestamp'
        version:
          type: object
          properties:
            structure:
              $ref: '#/components/schemas/VersionHash'
            weights:
              $ref: '#/components/schemas/VersionHash'
            weights_source:
              type: string
              enum:
                - pinned
                - default
            binding:
              type: integer
              minimum: 1
            engine_profile:
              type: string
          required:
            - structure
            - weights
            - weights_source
            - binding
            - engine_profile
          additionalProperties: false
        outcomes:
          type: object
          properties:
            below:
              type: string
              pattern: ^[a-z][a-z0-9_]{0,63}$
              description: The outcome for a score under the first cut.
            cuts:
              type: array
              items:
                type: object
                properties:
                  at:
                    type: number
                  outcome:
                    $ref: '#/components/schemas/Name'
                required:
                  - at
                  - outcome
                additionalProperties: false
              minItems: 1
              maxItems: 9
            abstain:
              $ref: '#/components/schemas/Name'
          required:
            - below
            - cuts
          additionalProperties: false
          description: The cut points as the caller sent them.
        score:
          type: number
        outcome:
          $ref: '#/components/schemas/Name'
        features:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/ExportedFeature'
          description: Each leaf's answer, by feature name.
        content:
          anyOf:
            - $ref: '#/components/schemas/ExportedContent'
            - type: 'null'
          description: The content judged; null when the decision does not store content.
      required:
        - id
        - item_id
        - assignment_mode
        - created_at
        - version
        - features
        - content
      additionalProperties: false
    ExportedSplit:
      type: string
      enum:
        - seed
        - train
        - burned
    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.
    VersionHash:
      type: string
      pattern: ^sha256:[0-9a-f]{64}$
    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}$
    AssignmentMode:
      type: string
      enum:
        - primary
        - shadow
        - backtest
        - seed
    Name:
      type: string
      pattern: ^[a-z][a-z0-9_]{0,63}$
    ExportedFeature:
      type: object
      properties:
        node_hash:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
          description: The hash of the prompt as it was asked.
        status:
          type: string
          enum:
            - answered
            - abstained
            - failed
        value:
          type: number
          minimum: 0
          maximum: 1
          description: The answer's probability of true, when answered.
        distribution:
          type: object
          additionalProperties:
            type: number
          description: >-
            The raw per-answer distribution as the engine returned it, never
            re-rounded.
        engine:
          type: string
        model:
          type: string
          description: The model the binding asked for.
        resolved_model:
          type: string
          description: The versioned model id that answered.
        adapter:
          type: string
          description: The adapter package and version.
        cached:
          type: boolean
          description: True when the answer came from the feature cache.
      required:
        - node_hash
        - status
        - cached
      additionalProperties: false
    ExportedContent:
      type: object
      properties:
        subject:
          type: array
          items:
            $ref: '#/components/schemas/ContentPart'
        context:
          type: array
          items:
            $ref: '#/components/schemas/ContentPart'
      required:
        - subject
      additionalProperties: false
    ContentPart:
      oneOf:
        - $ref: '#/components/schemas/TextPart'
        - $ref: '#/components/schemas/JsonPart'
      discriminator:
        propertyName: type
        mapping:
          text: '#/components/schemas/TextPart'
          json: '#/components/schemas/JsonPart'
    TextPart:
      type: object
      properties:
        type:
          type: string
          enum:
            - text
        text:
          type: string
          minLength: 1
        name:
          type: string
          pattern: ^[a-z][a-z0-9_]{0,63}$
          description: >-
            Names the part, so a feature's `inputs` can pick it. In `subject`,
            avoid "context" when the request also sends `context`.
        sensitive:
          type: boolean
          description: >-
            Display policy only: reviewers see the part blurred or behind a
            link. The engine still reads it and it is stored like any other
            part.
      required:
        - type
        - text
      additionalProperties: false
    JsonPart:
      type: object
      properties:
        type:
          type: string
          enum:
            - json
        data:
          anyOf:
            - type: object
              additionalProperties: {}
            - type: array
              items: {}
        name:
          type: string
          pattern: ^[a-z][a-z0-9_]{0,63}$
          description: >-
            Names the part, so a feature's `inputs` can pick it. In `subject`,
            avoid "context" when the request also sends `context`.
        sensitive:
          type: boolean
          description: >-
            Display policy only: reviewers see the part blurred or behind a
            link. The engine still reads it and it is stored like any other
            part.
      required:
        - type
        - data
      additionalProperties: false
  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.