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

# Retrieve the results snapshot

> Returns the latest results for a test: the same numbers you see on
that test's Analytics dashboard in the ABConvert admin, as of the
last time they were computed. Beta.

This read takes no date range and no filters: it answers for the
test's whole run so far. To narrow the window, group by a dimension,
or scope to a product group, use
[`POST /experiments/{id}/results`](/api-reference/results/create-a-custom-result-query).

ABConvert recomputes the numbers about every 6 hours while a test is
running. Polling faster returns the same numbers, with an unchanged
`computed_at`.
For fresher numbers,
[`POST /experiments/{id}/results`](/api-reference/results/create-a-custom-result-query)
computes on demand and answers with data at most an hour old.

Two runnable examples read this endpoint: the cookbook's
[Slack report](https://github.com/ABConvert/abconvert-cookbook/tree/main/examples/slack-report)
and its
[guardrail monitor](https://github.com/ABConvert/abconvert-cookbook/tree/main/examples/guardrail-monitor).

A paused test is not recomputed: it keeps the numbers it had when it
was paused. A test whose results have never been computed answers
202 with no body until the first set is ready. An ended test is not
recomputed either, so a test that ended with no results computed
keeps answering 202.




## OpenAPI

````yaml /api-reference/openapi.yaml get /experiments/{id}/results
openapi: 3.1.0
info:
  title: ABConvert Public API
  version: 1.0.0-beta
  description: |
    Create, read, update, and run A/B tests on your Shopify store from your
    own code.

    This version covers test CRUD, lifecycle actions, archiving, results,
    order exports, scheduling, and token scopes. There are no webhooks: to
    see a test's current state, call
    [`GET /experiments/{id}`](/api-reference/experiments/retrieve-a-test).

    Every path, field, and identifier in this API spells the resource
    `experiment`. An experiment is a test: the same object you create and
    run in the ABConvert admin. These docs say "test" in prose.

    A finding is one problem the server found, carrying a `code`, a
    `message`, and a `severity`. An `error` finding blocks the request and
    returns 422. A `warning` finding rides along with a successful response,
    in `warnings`.

    Conventions shared by every endpoint (identifiers, data formats,
    pagination, sparse updates, idempotency, rate limits, per-type support,
    and feature availability) live in the
    [API overview](/api-reference/overview). Tokens and scopes live in
    [Authentication](/api-reference/authentication).
servers:
  - url: https://api.abconvert.io/v1
security:
  - bearerAuth: []
tags:
  - name: Experiments
    description: Create, read, update, and list tests.
  - name: Lifecycle
    description: Status transitions. See the state machine in each action's description.
  - name: Results
    description: Read-only views of pipeline-computed snapshots. Beta.
  - name: Exports
    description: Async order-export jobs.
paths:
  /experiments/{id}/results:
    get:
      tags:
        - Results
      summary: Retrieve the results snapshot
      description: >
        Returns the latest results for a test: the same numbers you see on

        that test's Analytics dashboard in the ABConvert admin, as of the

        last time they were computed. Beta.


        This read takes no date range and no filters: it answers for the

        test's whole run so far. To narrow the window, group by a dimension,

        or scope to a product group, use

        [`POST
        /experiments/{id}/results`](/api-reference/results/create-a-custom-result-query).


        ABConvert recomputes the numbers about every 6 hours while a test is

        running. Polling faster returns the same numbers, with an unchanged

        `computed_at`.

        For fresher numbers,

        [`POST
        /experiments/{id}/results`](/api-reference/results/create-a-custom-result-query)

        computes on demand and answers with data at most an hour old.


        Two runnable examples read this endpoint: the cookbook's

        [Slack
        report](https://github.com/ABConvert/abconvert-cookbook/tree/main/examples/slack-report)

        and its

        [guardrail
        monitor](https://github.com/ABConvert/abconvert-cookbook/tree/main/examples/guardrail-monitor).


        A paused test is not recomputed: it keeps the numbers it had when it

        was paused. A test whose results have never been computed answers

        202 with no body until the first set is ready. An ended test is not

        recomputed either, so a test that ended with no results computed

        keeps answering 202.
      operationId: getExperimentResults
      parameters:
        - $ref: '#/components/parameters/ExperimentId'
        - name: breakdown
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/BreakdownDimension'
          description: |
            Omit for overall results. `date` adds a per-day breakdown
            alongside the overall totals.
      responses:
        '200':
          description: The current snapshot.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResultsSnapshot'
        '202':
          description: |
            No results have been computed for this test yet, and the response
            has no body. Wait the number of seconds in `Retry-After`, then ask
            again.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before asking again.
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
        default:
          $ref: '#/components/responses/UnexpectedError'
      security:
        - bearerAuth: []
components:
  parameters:
    ExperimentId:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: ^[0-9]+$
        example: '3021'
      description: The test's numeric ID as a string, for example `"3021"`.
  schemas:
    BreakdownDimension:
      type: string
      enum:
        - date
      example: date
      description: >
        `date` is the only breakdown this read offers, because it is the only

        one kept ready in advance. For any other dimension, run a custom

        result query with

        [`POST
        /experiments/{id}/results`](/api-reference/results/create-a-custom-result-query).
    ResultsSnapshot:
      type: object
      description: |
        A read-only view of the test's most recently computed results.
        Beta: the shape may change. The docs define each metric field.
      required:
        - object
        - experiment_id
        - computed_at
        - test_groups
      properties:
        object:
          type: string
          const: experiment_results
        experiment_id:
          type: string
          example: '3021'
        computed_at:
          type:
            - string
            - 'null'
          format: date-time
          description: |
            When the snapshot was computed. While no snapshot exists yet,
            the endpoint returns 202 instead of a snapshot.
        outcome:
          type:
            - string
            - 'null'
          enum:
            - winner
            - loser
            - inconclusive
            - insufficient_data
            - null
          description: Judged relative to Control.
        winning_test_group_index:
          type:
            - integer
            - 'null'
          example: 1
          description: Set when `outcome` is `winner`.
        srm_status:
          type:
            - string
            - 'null'
          enum:
            - ok
            - mismatch
            - insufficient_data
            - null
          description: >-
            Sample ratio mismatch check: whether observed traffic matches the
            configured splits.
        analysis:
          type: object
          description: How the `outcome` and `vs_control` comparisons were computed.
          properties:
            credible_interval_level:
              type: number
              example: 0.95
              description: Level of every Bayesian `credible_interval`.
            confidence_level:
              type: number
              example: 0.95
              description: Level of every frequentist `confidence_interval`.
        test_groups:
          type: array
          items:
            $ref: '#/components/schemas/TestGroupResults'
        breakdown:
          type:
            - object
            - 'null'
          description: Present when `breakdown` was requested.
          properties:
            dimension:
              $ref: '#/components/schemas/BreakdownDimension'
            rows:
              type: array
              items:
                allOf:
                  - $ref: '#/components/schemas/TestGroupResults'
                  - type: object
                    required:
                      - dimension_value
                    properties:
                      dimension_value:
                        type: string
    TestGroupResults:
      allOf:
        - $ref: '#/components/schemas/TestGroupMetrics'
        - type: object
          properties:
            orders:
              type:
                - integer
                - 'null'
              example: 179
            revenue:
              oneOf:
                - $ref: '#/components/schemas/Money'
                - type: 'null'
            vs_control:
              type:
                - object
                - 'null'
              description: |
                Comparison against Control, per metric. Null on the
                control's own row.
              properties:
                conversion_rate:
                  $ref: '#/components/schemas/MetricComparison'
                revenue_per_visitor:
                  $ref: '#/components/schemas/MetricComparison'
                average_order_value:
                  $ref: '#/components/schemas/MetricComparison'
                profit_per_visitor:
                  $ref: '#/components/schemas/MetricComparison'
                add_to_cart_rate:
                  $ref: '#/components/schemas/MetricComparison'
                reached_checkout_rate:
                  $ref: '#/components/schemas/MetricComparison'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - conflict_error
                - validation_error
                - rate_limit_error
                - api_error
            code:
              type: string
              description: |
                The code set is fixed for each error type.

                - 400: `malformed_json`, `unknown_parameter`,
                  `invalid_cursor`, `invalid_include`,
                  `invalid_idempotency_key` (an `Idempotency-Key` header that
                  is empty or over 255 characters), `invalid_request`
                  (a query parameter sent more than once, or `limit` not
                  a whole number between 1 and 100)
                - 401: `missing_token`, `invalid_token`, `token_revoked`
                - 403: `insufficient_scope`, `api_access_disabled`,
                  `subscription_inactive`, `billing_cap_reached`,
                  `feature_not_in_plan`, `carrier_service_required`
                - 404: `experiment_not_found`, `export_not_found`,
                  `results_query_not_found`
                - 409: `invalid_status_transition`, `locked_field`,
                  `idempotency_key_in_use`, `checks_pending`,
                  `editor_owned_content`
                - 413: `request_too_large` (the body is over 10 MB)
                - 422: read the `findings` array. Each entry's `param` names
                  the field and its `message` names what to fix; the top-level
                  `code` repeats the first blocking finding. Some codes aren't
                  finding rules:
                  `invalid_request` (a body or parameter the schema refuses),
                  `invalid_type` (a `type` this API can't create),
                  `invalid_configuration` (the test as stored isn't valid to
                  act on; `message` names what to fix),
                  `launch_blocked` (a launch check failed with no finding to
                  name), `preview_unsupported` (this test can't show a single
                  test group through a preview link; preview it in the
                  ABConvert admin), and
                  `export_not_available` (the test hasn't run yet, so there is
                  nothing to export).
                  A custom result query refuses its own parameters
                  with `invalid_breakdown_dimension`,
                  `breakdown_too_many_dimensions`,
                  `duplicate_breakdown_dimension`, `invalid_date_range`,
                  `date_range_out_of_bounds` (the window ends before the test
                  started, or begins after today), `sample_basis_unsupported`,
                  `product_group_not_found`,
                  `product_id_requires_product_group`, or
                  `results_not_available` (the test hasn't run yet).

                  A query parameter value outside the published vocabulary
                  (`status`, `type`, `scheduled`, a `created_at` bound) returns
                  `invalid_request` with `param` naming the parameter.

                  On create, the codes fall into four families:
                  - a resource this shop doesn't have:
                    `product_not_found`, `collection_not_found`,
                    `theme_not_found`, `template_not_found`,
                    `zone_not_found`, `rate_not_found`
                  - a test group with nothing to test, so add a change:
                    `template_required`,
                    `destination_required`, `change_required`,
                    `rate_required`, `offer_required`
                  - two parts of the request disagree, and the code names
                    which:
                    `duplicate_change`, `duplicate_group_name`,
                    `duplicate_rule_key`, `rule_key_not_found`,
                    `zone_not_declared`, `control_group_invalid`,
                    `split_sum_invalid`
                  - a value this API won't write, so author it in the
                    ABConvert admin instead:
                    `currency_mismatch`, `control_offers_rates`,
                    `match_type_unsupported`,
                    `max_discount_amount_not_allowed`,
                    `discount_combination_invalid`,
                    `condition_range_invalid`, `product_limit_exceeded`,
                    `market_pricing_incomplete`
                - 429: `rate_limited`
                - 500: `internal_error`
                - 501: `not_implemented` (a test type this API can't create
                  yet, or a test whose stored form this API can't read back;
                  use the ABConvert admin instead)
                - 503: `rate_limit_unavailable` (the limiter could not be
                  reached, so the write was refused rather than let through
                  uncounted; reads are served in this case),
                  `audit_unavailable` (the write could not be recorded in the
                  audit log, so it was not performed),
                  `idempotency_unavailable` (ABConvert could not check your
                  `Idempotency-Key`, so the request was refused rather than run
                  without replay protection; retry with the same key),
                  `internal_error` (the request could not be run alongside
                  other changes to this shop, so nothing was attempted; retry)
                - 504: `internal_error` (the write took too long and was rolled
                  back, so nothing changed)

                Every 5xx carries a `request_id`. Quote it when you contact
                support.
            message:
              type: string
            param:
              type:
                - string
                - 'null'
              description: |
                JSON path of the offending request field. On
                `feature_not_in_plan` it carries the feature slug instead:
                `price`, `offer`, `multi_market`, or `checkout_blocks`.
            request_id:
              type: string
              description: |
                Correlation id for one refused request. Present on every 5xx
                and on nothing else: a 4xx is about the request, and there is
                nothing on our side to look up. Send an `x-correlation-id`
                header and that value is the id you get back.
            details:
              type: object
              additionalProperties: true
              description: |
                Machine-readable context, present where a code defines it.
                `invalid_status_transition` carries
                `{"current_status": "...", "allowed_actions": [...]}` so a
                client can branch without parsing `message`.
            findings:
              type: array
              description: Present on `validation_error`.
              items:
                $ref: '#/components/schemas/Finding'
    TestGroupMetrics:
      type: object
      required:
        - test_group_index
        - sample_size
      properties:
        test_group_index:
          type: integer
          example: 1
        sample_size:
          type: integer
          example: 5231
          description: |
            Visitors in the test group, or exposures for exposure-measured
            types. This is the denominator for `conversion_rate`,
            `revenue_per_visitor`, and `profit_per_visitor`.
        session_count:
          type:
            - integer
            - 'null'
          example: 6816
          description: |
            Sessions from the test group's traffic. This is the denominator
            for `add_to_cart_rate` and `reached_checkout_rate`.
        conversion_rate:
          type:
            - number
            - 'null'
          example: 0.034
        revenue_per_visitor:
          oneOf:
            - $ref: '#/components/schemas/Money'
            - type: 'null'
          description: |
            Money, in the shop's own currency.
        average_order_value:
          oneOf:
            - $ref: '#/components/schemas/Money'
            - type: 'null'
        profit_per_visitor:
          oneOf:
            - $ref: '#/components/schemas/Money'
            - type: 'null'
          description: Null until COGS settings are configured.
        add_to_cart_rate:
          type:
            - number
            - 'null'
          example: 0.081
        reached_checkout_rate:
          type:
            - number
            - 'null'
          example: 0.052
    Money:
      type: object
      required:
        - amount
        - currency
      properties:
        amount:
          type: string
          pattern: ^-?\d+(\.\d+)?$
          example: '17.99'
          description: >-
            A decimal string. Only fields that express a delta accept a leading
            minus sign.
        currency:
          type: string
          pattern: ^[A-Z]{3}$
          example: USD
          description: |
            ISO 4217 code. On write it must be the shop's own currency;
            anything else returns 422 `currency_mismatch`.
    MetricComparison:
      type: object
      description: Comparison of one metric against Control.
      properties:
        lift:
          type:
            - number
            - 'null'
          example: 0.062
          description: >
            Relative change against Control, as a fraction of the magnitude of

            Control's value. `0.062` means +6.2%. The sign follows the absolute

            difference, so a test group that improves on a negative Control
            (which

            `profit_per_visitor` reaches when costs exceed revenue) reports a

            positive lift.


            Null when no ratio exists: Control's value is zero, or Control

            and this test group sit on opposite sides of zero. Read

            `difference`, which is defined in both cases.
        difference:
          oneOf:
            - $ref: '#/components/schemas/Quantity'
            - type: 'null'
          description: |
            Absolute difference against Control, in the metric's own unit.
            Defined wherever `lift` is null, so this is what to read to know
            which way a result went, and the only comparison a test group
            straddling zero carries. Null only when Control or this test group
            has no value for the metric.
        bayesian:
          type:
            - object
            - 'null'
          description: |
            Bayesian comparison. Null when Bayesian analysis is
            unavailable for this snapshot. `frequentist` can be null too:
            it is absent on a test group with too little data to compute
            statistics, and on every breakdown row.
          properties:
            credible_interval:
              type:
                - object
                - 'null'
              description: |
                Credible interval on `lift`, at
                `analysis.credible_interval_level`. Null wherever `lift` is
                null, and on a snapshot that stored no interval. Read
                `difference_interval`, which is in the metric's own unit and
                needs no baseline.
              properties:
                lower:
                  type: number
                  example: -0.011
                upper:
                  type: number
                  example: 0.134
            prob_beat_control:
              type:
                - number
                - 'null'
              minimum: 0
              maximum: 1
              example: 0.94
              description: Probability that this test group beats Control on this metric.
            risk:
              oneOf:
                - $ref: '#/components/schemas/Quantity'
                - type: 'null'
              description: |
                Expected loss if you ship this test group and it is actually
                worse than Control, in the metric's own units, not a ratio.
                On a conversion rate of 0.032, `0.004` is four tenths of a
                percentage point, not 0.4 percent. Being in the metric's own
                units, it is `Money` on a money-valued metric.
        frequentist:
          type:
            - object
            - 'null'
          description: Frequentist comparison.
          properties:
            p_value:
              type:
                - number
                - 'null'
              minimum: 0
              maximum: 1
              example: 0.081
              description: Two-sided p-value on the difference against Control.
            confidence_interval:
              type:
                - object
                - 'null'
              description: |
                Confidence interval on `lift`, at `analysis.confidence_level`.
                Null wherever `lift` is null, and on a snapshot that stored
                no interval. Read `difference_interval`, which is in the
                metric's own unit and needs no baseline.
              properties:
                lower:
                  type: number
                  example: -0.008
                upper:
                  type: number
                  example: 0.131
            difference_interval:
              oneOf:
                - $ref: '#/components/schemas/QuantityInterval'
                - type: 'null'
              description: |
                Confidence interval on `difference`, at
                `analysis.confidence_level`, in the metric's own unit.
                `confidence_interval` is this interval divided by the
                absolute value of Control's value.

                Present wherever `lift` is null but the snapshot carries a
                difference. Null only when the snapshot stores no interval
                at all.
    Finding:
      type: object
      required:
        - severity
        - code
        - message
      properties:
        severity:
          type: string
          enum:
            - error
            - warning
        code:
          type: string
          description: |
            A snake_case identifier for the finding. Published codes never
            change, and new ones are only added.
          enum:
            - product_not_found
            - collection_not_found
            - theme_not_found
            - template_not_found
            - zone_not_found
            - rate_not_found
            - market_not_found
            - rule_key_not_found
            - template_required
            - destination_required
            - change_required
            - rate_required
            - offer_required
            - theme_required
            - market_empty
            - product_not_testable
            - theme_not_testable
            - duplicate_change
            - duplicate_group_name
            - duplicate_rule_key
            - duplicate_trigger_url
            - duplicate_theme
            - duplicate_template
            - control_group_invalid
            - control_offers_rates
            - control_redirects
            - control_runs_code
            - split_sum_invalid
            - test_groups_too_many
            - zone_not_declared
            - discount_combination_invalid
            - currency_mismatch
            - match_type_unsupported
            - max_discount_amount_not_allowed
            - condition_range_invalid
            - condition_unit_invalid
            - product_limit_exceeded
            - country_limit_exceeded
            - rule_limit_exceeded
            - market_pricing_incomplete
            - path_invalid
            - rate_name_required
            - rate_price_invalid
            - offer_title_required
            - filter_value_invalid
            - filter_operator_invalid
            - filter_key_required
            - filter_type_unsupported
            - custom_js_invalid
            - split_changed
            - country_served_by_other_market
            - country_ownership_ambiguous
            - market_not_contextual
            - trigger_overlaps
            - destination_matches_trigger
            - zone_not_configured
            - theme_not_published
            - filter_value_unrecognized
            - test_groups_missing
            - split_invalid
            - theme_test_running
            - visual_editor_conflict
            - app_embed_disabled
            - resource_claimed
            - test_overrides_personalization
            - audience_empty
            - force_assign_group_invalid
            - block_placement_invalid
            - modification_invalid
            - modification_kind_unknown
            - modification_duplicate_singleton
            - launch_check_failed
        param:
          type:
            - string
            - 'null'
          description: |
            JSON path of the field the finding points at, for example
            `shared.price.markets[0].countries[2]`. Offending values appear
            in `message`, not here.
        message:
          type: string
    Quantity:
      description: |
        A value in the metric's own unit: `Money` for a money-valued metric
        (`revenue_per_visitor`, `average_order_value`, `profit_per_visitor`,
        `revenue`), a bare number for a dimensionless one (a rate, a count).
        The shape is fixed per field, never data-dependent.

        Money amounts can carry more decimal places than the currency's
        minor units: a per-visitor difference is routinely sub-cent. Parse
        as a decimal, never assume two places.
      oneOf:
        - $ref: '#/components/schemas/Money'
        - type: number
    QuantityInterval:
      type: object
      description: An interval whose bounds are in the metric's own unit.
      properties:
        lower:
          $ref: '#/components/schemas/Quantity'
        upper:
          $ref: '#/components/schemas/Quantity'
  headers:
    XRateLimitLimit:
      description: |
        How many requests this token may make in the current 60-second window.
        Reads, writes, and result queries each have a separate budget, so this
        is the budget for the class this request falls in.
      schema:
        type: integer
    XRateLimitRemaining:
      description: Requests left in the current window.
      schema:
        type: integer
    XRateLimitReset:
      description: Unix timestamp when the window resets.
      schema:
        type: integer
  responses:
    BadRequest:
      description: |
        `invalid_request_error`. The request never reached the test, so nothing
        changed. Fix the request and send it again.

        - `malformed_json`: the body isn't valid JSON.
        - `unknown_parameter`: `param` names a field this endpoint doesn't
          accept. Remove it.
        - `invalid_cursor`: the cursor is stale or invalid. Start the list
          again from the first page.
        - `invalid_include`: `include` accepts `results_summary` only.
        - `invalid_idempotency_key`: send a key of 1 to 255 characters, or omit
          the header.
        - `invalid_request`: `param` names the query parameter to fix.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: |
        `authentication_error`. Send a valid token as
        `Authorization: Bearer <token>`.

        - `missing_token`: no bearer token on the request.
        - `invalid_token`: the token is malformed, does not exist, or names a
          shop ABConvert can no longer act for.
        - `token_revoked`: someone revoked this token in the ABConvert admin.
          Create a new one.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >
        `permission_error`. The shop or the token isn't allowed to do this, so

        nothing changed.


        | Code | What to do | Where it applies |

        |---|---|---|

        | `insufficient_scope` | Use a token with the scope named in
        `details.required_scope`. | Any request |

        | `api_access_disabled` | API access is turned off for this shop.
        Contact support. | Any request |

        | `subscription_inactive` | Renew the shop's ABConvert subscription,
        then retry. | `start` and `resume` |

        | `billing_cap_reached` | Raise the shop's usage cap for the period,
        then retry. | `start` and `resume` |

        | `feature_not_in_plan` | Upgrade to the plan named in `message`.
        `param` names the feature: `price`, `offer`, `multi_market`, or
        `checkout_blocks`. | Create, update, `preview`, `start`, `resume` |

        | `carrier_service_required` | Give the shop carrier-calculated
        shipping. A Shopify plan with the feature, an existing carrier service,
        or annual ABConvert billing each satisfy it. | Shipping tests |


        `pause`, `end`, and `archive` never return entitlement errors. See

        [Feature availability](/api-reference/overview#feature-availability).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: |
        `not_found_error`. No resource with that ID belongs to this shop. Check
        the ID, and check that the token belongs to the shop that owns the
        resource: a token reaches one shop only.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationFailed:
      description: >
        `validation_error`. On a request body, the `findings` array lists

        every blocking finding. On a query string, the error carries `param`

        naming the parameter, and no `findings`.


        `preview_unsupported` is the one code here that judges the test rather

        than the request: see

        [`POST
        /experiments/{id}/preview`](/api-reference/lifecycle/preview-a-test). It
        carries

        neither `findings` nor `param`, because nothing about the request is

        wrong.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: |
        `rate_limit_error`, code `rate_limited`. You sent more requests than
        your budget allows. Wait the number of seconds in `Retry-After`, then
        retry.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the window resets.
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnexpectedError:
      description: |
        A status this operation doesn't enumerate. The body is the standard
        error shape.

        - 413 `request_too_large`: the body is over 10 MB. Send less.
        - 500 `internal_error`: something went wrong on our side. Retry. If it
          keeps failing, send support the `request_id`.
        - 503: the request was refused before it ran, so nothing on the shop
          changed. Retry it, with the same `Idempotency-Key` if you sent one.
          The code names which safeguard was unavailable:
          `rate_limit_unavailable`, `audit_unavailable`,
          `idempotency_unavailable`, or `internal_error`.
        - 504 `internal_error`: the write timed out and nothing changed. Retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Bearer tokens created in the ABConvert admin under
        Settings → MCP & API Access. Scopes:
        `read_experiments` and `write_experiments` (write implies read; the
        default is read). See
        [Authentication](/api-reference/authentication) for which scope each
        request needs.

````