---
title: "Evaluate a scenario"
method: POST
path: "/v25-10-beta/agentic-logic/rules/{ruleId}/evaluate-scenario"
tags: ["Agentic Logic"]
---

# Evaluate a scenario

`POST /v25-10-beta/agentic-logic/rules/{ruleId}/evaluate-scenario`

## Request

- Base URL: `https://api.intelligems.io`
- URL: `https://api.intelligems.io/v25-10-beta/agentic-logic/rules/{ruleId}/evaluate-scenario`
- Auth: API key in header `intelligems-access-token`
- TypeScript: <https://skmtc.dev/intelligems/apis/intelligems-external-api-v25-10-beta/docs/v25-10-beta/agentic-logic/rules/:ruleId/evaluate-scenario/post.ts> (a function with typed inputs; its response is checked against the schema)

```http
POST /v25-10-beta/agentic-logic/rules/{ruleId}/evaluate-scenario
```

Runs one scenario against the rule's saved draft (code rules) or current
template configuration (builder rules), under the rule's current settings.
nothing is persisted.

- `scenario` (object, required) — The full scenario. `request.placementId`
  defaults to the rule's own id.
- A code rule with no saved draft source answers 404.

The response is the admin's scenario preview (`result`, `exclusions`,
`coverage`, `hydratedItems`, `onEmpty`, `evaluated`), plus `logs` for code
rules:

- `logs` — What the rule's `logger` calls wrote during this run, in call
  order, as `{ level, message, details?, invocationId?, timestamp }`. Logs are
  returned when the rule completes, when it returns a malformed envelope
  (`errorClass: "invalid_envelope"`), when it throws, and when it exceeds the
  budget. They are `[]` when the rule logged nothing. A failed run whose output
  exceeds 256KB keeps only its earliest and latest entries, and the response
  then carries `logsTruncated: true`. A failure on the
  platform side, such as a failed SDK read, is rethrown by the sandbox runner
  without its output, so it returns an error result with no `logs` key. That
  includes a throw after the rule caught a failed SDK read; a budget overrun
  after one still returns its `logs`. Plain
  `console.log` output is not included. Builder rules have no `logs` key.
- `timestamp` is ISO-8601 UTC host wall-clock time at the moment `logger` was
  called, with millisecond precision. Subtract two entries' timestamps to get
  the elapsed time between those calls, for example around a branch or a read.

A code rule's preview runs under a fixed 10000ms budget
(`AGENTIC_LOGIC_RUNTIME_BUDGET_MAX_MS`), whatever budget its placements
assign. The budget is wall-clock time for the whole `evaluate` call, SDK reads
included. A run that exceeds it returns an error result with
`errorClass: "timeout"` whose warning contains
`Rule exceeded runtime ceiling of 10000ms`. Its `logs` hold the entries written
before the timeout.

Use the `intelligems-access-token` header. The token selects the organization; do not send `orgId`. The organization must have Agentic Logic enabled.

**Rate Limit:** 50 requests per 30s window, refilling 25 tokens every 30s.

## Path parameters

- `ruleId` string, uuid, required

## Request body

- object
  - `scenario` object, required
    - `name` string, required: Human-readable scenario name, shown in test results and gate details.
    - `request` object, required: The recommendation request the scenario evaluates. `placementId` and `surface` here are wire shape, not references: gates evaluate the rule directly, never an assignment, so no real placement is involved.
      - `surface` 'onsite' | 'checkout' | 'post-purchase', required: Surface the recommendation will render on. There is deliberately no 'headless' value — API callers declare the surface they render.
      - `cart` object, nullable, required: Snapshot of the visitor's current cart. An empty cart (zero lines) is a legal cart — distinct from cart: null, which means the surface has no cart at all (post-purchase).
        - `currency` string, required: ISO-4217 currency code all money fields in this cart are in.
        - `subtotal` integer, required: Cart merchandise subtotal in integer minor units of `currency`, before shipping and taxes.
        - `itemCount` integer, required: Total unit quantity across all lines (sum of line quantities) — units, not the count of distinct lines.
        - `lines` object[], required: The cart lines.
          - `productId` string, required: Shopify product GID (gid://shopify/Product/<n>). The bare numeric id the Shopify admin and most PIM exports show is accepted and promoted to GID form on the way in; anything that is neither is rejected.
          - `variantId` string, required: Shopify variant GID (gid://shopify/ProductVariant/<n>). The bare numeric id the Shopify admin and most PIM exports show is accepted and promoted to GID form on the way in; anything that is neither is rejected.
          - `quantity` integer, required: Number of units on the line. Positive integer.
          - `unitPrice` integer, required: Per-unit price actually in the cart, in integer minor units of the cart's currency.
          - `lineTotal` integer, required: Line total (unit price times quantity, after line-level adjustments), in integer minor units of the cart's currency.
          - `selectedOptions` object[], required: Selected product options for the line's variant.
            - `name` string, required: Option name as configured on the Shopify product (e.g. 'Size').
            - `value` string, required: Selected option value (e.g. 'Medium').
          - `isSubscriptionLine` boolean, required: True when the line carries a selling plan (subscription), false for a one-time purchase.
          - `flags` object, required: Provenance flags (gift card, synthetic gift, bundle child, Intelligems upsell). Which flags a surface can actually answer differs per surface — see the request contract's per-surface caveats.
            - `giftCard` boolean, nullable, required: True when the line is a Shopify gift-card product, false when known not to be one, or null when the surface exposes no authoritative marker. NULL on every checkout and post-purchase line — neither surface exposes a gift-card marker, so a clause keyed on this flag is inert there.
            - `syntheticGift` boolean, nullable, required: True when the line was injected as a gift-with-purchase by a promotion, false when known not to be one, or null when the surface exposes no authoritative marker.
            - `bundleChild` boolean, nullable, required: True when the line is a child component of an expanded bundle, false when known not to be one, or null when the surface exposes no authoritative marker. NULL on every post-purchase line, and never `true` onsite — `/cart.js` does not list bundle component lines at all.
            - `igUpsell` boolean, nullable, required: True when the line was added by an Intelligems upsell, false when known not to be one, or null when the surface exposes no authoritative marker.
          - `tags` string[]: The line product's Shopify tags, hydrated ONLY when an evaluated rule targets on tags — absent otherwise. Absent means 'not looked up' and an empty array means 'looked up, none'; the two are never interchangeable, and a rule reading an absent value must fail loudly rather than treat it as no tags.
          - `collections` string[]: Shopify collection GIDs the line product belongs to, hydrated ONLY when an evaluated rule targets on collections — absent otherwise. Bare numeric collection ids are accepted and promoted to GID form on the way in. Same absent-vs-empty rule as `tags`.
      - `order` object, nullable, required: The completed order a post-purchase surface is rendered against. First-class on the post-purchase surface only; null everywhere else. Deliberately carries NO customer field — the top-level request `customer` is the single customer reference.
        - `orderId` string, required: Shopify order GID (gid://shopify/Order/<n>).
        - `checkoutToken` string: Shopify checkout token for the checkout that produced this order, when available.
        - `createdAt` string, date-time, required: Order creation time as an ISO-8601 UTC timestamp.
        - `currencyCode` string, required: ISO-4217 shop currency code the order's money fields are denominated in.
        - `presentmentCurrency` string, required: ISO-4217 currency code the shopper saw at checkout, which may differ from `currencyCode`.
        - `itemsSubtotal` integer, required: Merchandise subtotal after line-level discounts but before order-level discounts, shipping, and taxes. Integer minor units.
        - `discountTotal` integer: Total order-level discount amount in integer minor units, when any.
        - `shippingTotal` integer: Shipping charged in integer minor units, when known.
        - `taxTotal` integer: Taxes charged in integer minor units, when known.
        - `total` integer, required: What the shopper was actually charged, in integer minor units.
        - `lines` object[], required: The purchased order lines.
          - `productId` string, required: Shopify product GID (gid://shopify/Product/<n>). The bare numeric id the Shopify admin and most PIM exports show is accepted and promoted to GID form on the way in; anything that is neither is rejected.
          - `variantId` string, required: Shopify variant GID (gid://shopify/ProductVariant/<n>). The bare numeric id the Shopify admin and most PIM exports show is accepted and promoted to GID form on the way in; anything that is neither is rejected.
          - `quantity` integer, required: Number of units on the line. Positive integer.
          - `unitPrice` integer, required: Per-unit price actually in the cart, in integer minor units of the cart's currency.
          - `lineTotal` integer, required: Line total (unit price times quantity, after line-level adjustments), in integer minor units of the cart's currency.
          - `selectedOptions` object[], required: Selected product options for the line's variant.
            - `name` string, required: Option name as configured on the Shopify product (e.g. 'Size').
            - `value` string, required: Selected option value (e.g. 'Medium').
          - `isSubscriptionLine` boolean, required: True when the line carries a selling plan (subscription), false for a one-time purchase.
          - `flags` object, required: Provenance flags (gift card, synthetic gift, bundle child, Intelligems upsell). Which flags a surface can actually answer differs per surface — see the request contract's per-surface caveats.
            - `giftCard` boolean, nullable, required: True when the line is a Shopify gift-card product, false when known not to be one, or null when the surface exposes no authoritative marker. NULL on every checkout and post-purchase line — neither surface exposes a gift-card marker, so a clause keyed on this flag is inert there.
            - `syntheticGift` boolean, nullable, required: True when the line was injected as a gift-with-purchase by a promotion, false when known not to be one, or null when the surface exposes no authoritative marker.
            - `bundleChild` boolean, nullable, required: True when the line is a child component of an expanded bundle, false when known not to be one, or null when the surface exposes no authoritative marker. NULL on every post-purchase line, and never `true` onsite — `/cart.js` does not list bundle component lines at all.
            - `igUpsell` boolean, nullable, required: True when the line was added by an Intelligems upsell, false when known not to be one, or null when the surface exposes no authoritative marker.
          - `tags` string[]: The line product's Shopify tags, hydrated ONLY when an evaluated rule targets on tags — absent otherwise. Absent means 'not looked up' and an empty array means 'looked up, none'; the two are never interchangeable, and a rule reading an absent value must fail loudly rather than treat it as no tags.
          - `collections` string[]: Shopify collection GIDs the line product belongs to, hydrated ONLY when an evaluated rule targets on collections — absent otherwise. Bare numeric collection ids are accepted and promoted to GID form on the way in. Same absent-vs-empty rule as `tags`.
          - `title` string: Product title at purchase time, when available.
          - `vendor` string: Product vendor at purchase time, when available.
          - `productType` string: Shopify product type at purchase time, when available.
          - `sellingPlanId` string: Shopify selling-plan GID (gid://shopify/SellingPlan/<n>) when the line was purchased on a subscription.
          - `discountAllocations` object[]: Line-level discount allocations applied to this line, when any.
            - `amount` integer, required: Discount amount allocated to the line, in integer minor units of the order's currency.
            - `title` string: Discount title or code as shown on the order, when available.
      - `pageProduct` object, nullable, required: The product a PDP placement is rendered on. Populated for onsite product-page placements only; null everywhere else.
        - `productId` string, required: Shopify product GID of the product being viewed (gid://shopify/Product/<n>). The bare numeric id the Shopify admin shows is accepted and promoted to GID form on the way in; anything that is neither is rejected.
        - `handle` string, required: Shopify product handle (URL slug) of the product being viewed.
        - `targeting` object, nullable: Theme product facts used for onsite product targeting. Null when the theme has no product data; product-targeted branches then do not match. Absent from clients that predate the field; serving then reads the facts from Shopify when the rule targets the product.
          - `tags` string[], required
          - `collectionIds` integer[], required
          - `inventory` integer, required
          - `lowestVariantPrice` integer, required
      - `customer` object, nullable, required: Minimal identified-customer context, or null for guests. PII-MINIMAL GUARANTEE: this object carries ids, counts, and money aggregates ONLY — no email, name, phone, or address field may ever be added here. That is a contract property, not a data-availability accident; richer profile data is fetched at rule runtime via sdk.getCustomer().
        - `id` string, required: Shopify customer GID (gid://shopify/Customer/<n>).
        - `ordersCount` integer, nullable, required: Number of orders this customer has placed, or null when the surface did not supply it — checkout supplies it for a verified buyer when the app's protected-customer-data grant covers it; onsite and post-purchase never do. Integer, >= 0, when present. Null-check it, or use sdk.getCustomer() for a count that does not depend on the grant.
        - `amountSpent` object: Lifetime spend aggregate, when known.
          - `amount` integer, required: Lifetime amount spent in integer minor units of `currency`.
          - `currency` string, required: ISO-4217 currency code `amount` is denominated in.
      - `shipping` object, required: Shipping-incentive context. Required — 'not-applicable' is expressed in-band via its state field, never by omitting the object.
        - `state` 'resolved' | 'assumed' | 'not-applicable' | 'unavailable', required: 'resolved' = thresholds are known facts for this visitor; 'assumed' = best-effort defaults that may be wrong, so treat them as a hint rather than a fact when steering; 'not-applicable' = the store has no shipping incentive here, and the order may not be shipping at all; 'unavailable' = the shipping facts could not be read before the serving deadline (a Shopify read failed or timed out), so no threshold field is present and nothing is known — never read it as 'no incentive'.
        - `currency` string, required: ISO-4217 currency code the threshold fields are denominated in. Always present, even when state is 'not-applicable'.
        - `freeShippingThreshold` integer: Cart subtotal at which shipping becomes free, in integer minor units — pre-resolved to the visitor's assigned variation's threshold. Absent when there is no free-shipping incentive.
        - `remainingToFreeShipping` integer, nullable: How much more the visitor must add to qualify for free shipping, in integer minor units. 0 = already qualifies; absent or null = unresolvable.
        - `tiers` object[]: Tiered incentive ladder in ascending threshold order, when the store runs tiered incentives.
          - `threshold` integer, required: Cart subtotal at which this tier unlocks, in integer minor units of the shipping context's currency.
          - `label` string, required: Shopper-facing label for the tier (e.g. 'Free shipping').
        - `delivery` object: The delivery options Shopify computed for this checkout, as observed facts rather than merchant settings. Present only at checkout, and only once Shopify has computed options (an address or a pickup selection exists); absent onsite, post-purchase, and on first render. Present under every state, including 'not-applicable' for local pickup.
          - `isLocalPickup` boolean, required: True when every selected delivery option is a pickup option — the buyer is not shipping anything.
          - `selected` object: The buyer's currently selected option, when exactly one delivery group has a selection. Absent before the buyer picks, and when multiple groups (a subscription plus a one-time purchase) select different options — read `options` then.
            - `title` string: Shopper-facing option title, when Shopify reports one.
            - `isPickup` boolean, required: True for a pickup option (pickup point or pickup location) rather than a shipping rate. Pickup options carry no cost and no transit estimate.
            - `selected` boolean, required: True when this is the buyer's currently selected option in its delivery group.
            - `cost` integer: The option's cost after shipping discounts, in integer minor units of `currency`. 0 = free. Absent on pickup options, which have no cost.
            - `currency` string: ISO-4217 currency code of `cost`. Absent with it.
            - `carrierName` string: Carrier presenting the option, when Shopify reports one.
            - `transitDays` object: Estimated time in transit in whole days, when Shopify reports one — `min` rounded down, `max` rounded up. Shipping options only.
              - …
          - `options` object[], required: Every option Shopify computed, flattened across delivery groups, in Shopify's order. A zero-cost non-pickup option means the buyer already qualifies for free shipping.
            - `title` string: Shopper-facing option title, when Shopify reports one.
            - `isPickup` boolean, required: True for a pickup option (pickup point or pickup location) rather than a shipping rate. Pickup options carry no cost and no transit estimate.
            - `selected` boolean, required: True when this is the buyer's currently selected option in its delivery group.
            - `cost` integer: The option's cost after shipping discounts, in integer minor units of `currency`. 0 = free. Absent on pickup options, which have no cost.
            - `currency` string: ISO-4217 currency code of `cost`. Absent with it.
            - `carrierName` string: Carrier presenting the option, when Shopify reports one.
            - `transitDays` object: Estimated time in transit in whole days, when Shopify reports one — `min` rounded down, `max` rounded up. Shipping options only.
              - …
      - `country` string, nullable: OPTIONAL. ISO-3166-1 alpha-2 code of the market this request should be evaluated in, e.g. 'CA'. Omit it (or send null) and the platform evaluates in its default market, 'US'. Name it whenever the cart is denominated in a non-shop currency, so the catalog reads run in the same market the money does.
      - `cartFingerprint` string: Opaque client-supplied fingerprint of the cart state this request was built from. The engine echoes it verbatim on the result envelope so the rendering surface can compare and discard late results.
      - `placementId` string: Optional at authoring time; omit it and the server fills the rule's own id when the draft is saved. Wire shape only — publish gates evaluate the rule directly, never a placement assignment, so no real placement is needed and the author should never be asked for one.
    - `context` object: Optional evaluation context override. When absent the harness supplies a representative preview context.
      - `isPreview` boolean, required: True when the evaluation is a preview (editor/test-lab) run rather than live traffic.
      - `slotsNeeded` integer, required: How many recommendation slots the placement wants filled. Non-negative integer, at most 50 — ZERO is reachable, because a placement can resolve to no slots, so a rule must answer zero with an empty envelope before it reads anything.
      - `excludeOutOfStock` boolean: The rule's out-of-stock setting. When true, every SDK catalog read and product recommendation drops products with no variant for sale, so rule code never filters stock itself. Defaults to true, the rule setting's own default. A read that passes its own `excludeOutOfStock` overrides it.
    - `evaluatedAt` integer: Optional author-pinned evaluation instant, epoch milliseconds UTC. When absent the harness stamps the publish job's own instant. Pin it to make a time-dependent scenario stable: a case like 'a 30-day supply ordered 34 days ago' needs both ends fixed, or its expected verdict drifts with the calendar.
    - `description` string: Display copy for scenario pickers, phrased for a merchant (e.g. 'A cart that matches your matching cover-ups rule'). Absent on scenarios authored before display copy existed.
    - `targetBranchId` string, nullable: The branch this scenario is designed to fire: the graph branch node id (=== the attempt() base label). Null marks the nothing-matches scenario whose cart clears no branch; absent means unannotated (typically hand-authored).
    - `origin` 'authored' | 'generated': 'authored' = written by a person or authoring agent alongside the rule; 'generated' = produced by the scenario-generation service. Regeneration replaces only 'generated' scenarios and never clobbers authored ones.

## Response `200`

OK

- object: The scenario preview, plus the logger output of a code rule's run.
  - `result` object, required
    - `envelope` object, required: What a recommendation rule returns. Conventions: ids are Shopify GIDs; money is integer minor units with explicit ISO-4217 currency; timestamps are ISO-8601 UTC. Engine errors and timeouts collapse to an empty result behaviorally (the surface follows the placement's onEmpty policy) but stay distinct in evaluation telemetry.
      - `version` 1, required: Envelope schema version. Activating any reserved shape bumps this version — consumers negotiate on it, it never breaks silently. Independent of the REST API path version.
      - `items` object[], required: Recommended items in render order. An empty array is a valid result — a miss, not an error.
        - `kind` 'product' | 'variant' | 'image' | 'reviewRef' | 'metaobjectRef' | 'bundle', required: Item kind. Consumers ignore kinds they don't understand — never errors.
        - `id` string, required: Shopify GID of the recommended entity, scoped by `kind` (e.g. gid://shopify/Product/<n> for kind 'product'). For kind 'bundle' the id is the Product GID of the bundle's parent/anchor product, never a merchant-chosen key — there is no gid://shopify/Bundle/<n>.
        - `defaultVariantId` string: Which variant is pre-selected for the shopper — the core output for product-kind items, so surfaces render a purchasable default without re-deriving one.
        - `allowedVariantIds` string[]: Exhaustive list of variant GIDs the shopper may add, when the merchant restricted the item to a subset of its variants. ABSENT means unrestricted (every variant is allowed); present means only these are. Surfaces that mint per-variant discount signatures MUST sign only these — treating absence and an empty list alike would mint a signature for a variant the merchant excluded.
        - `sellingPlanId` string: Shopify selling-plan GID to attach when the item is added, for subscription recommendations. RESERVED in V1.
        - `intent` 'add' | 'replace': 'add' adds the item to the cart. 'replace' asks the surface to swap out the originating cart line and is materialized by NO surface today — emitting it is refused at publish rather than silently downgraded, because a surface that can only Add would put the swap target alongside the item it was meant to replace. Recommend the swap target with intent 'add', or wait for a surface that supports it. RESERVED in V1.
        - `discount` object: RESERVED in V1 and read by NO surface — setting it prices nothing, and it is REFUSED at ingress. Pricing is the placement's, never the rule's. Activating this field bumps the envelope version.
          - `type` 'percentage' | 'fixed', required: 'percentage' or 'fixed'.
          - `value` number, required: For 'percentage': percent points (10 = 10% off). For 'fixed': integer minor units in the shop currency.
        - `sourceBranch` string: Path-capable label of the rule branch that produced this item (e.g. 'colour-match/size-in-stock'), for telemetry and debugging.
        - `children` object[]: Child lines when kind is 'bundle'. Each child's `id` must match its own `kind` — a 'product' child carries a Product GID, a 'variant' child a ProductVariant GID. Schema-level tolerance: presence on other kinds is a gate concern, not a parse error.
          - `kind` 'product' | 'variant' | 'image' | 'reviewRef' | 'metaobjectRef' | 'bundle', required: Kind of the child item (scopes how `id` is interpreted).
          - `id` string, required: Shopify GID of the child, scoped by `kind`.
          - `quantity` integer, required: Units of the child included in the bundle. Positive integer.
      - `branchFired` object: The terminal rule branch that produced this result.
        - `base` string, required: The literal label passed to attempt('label') in the rule source — statically lexable, so publish gates can verify it exists in the code.
        - `instance` string: Runtime instance qualifier when one branch runs multiple passes (e.g. the tag this pass ran against). Present ONLY when the attempt supplied one via `attempt(base, fn, { instance })`; the key is omitted entirely otherwise, so consumers must treat its absence as normal and must never assert deep equality against an object that assumes it.
      - `cartFingerprint` string: Echo of the request's cartFingerprint, verbatim. The rendering surface compares it against the current cart state and discards late results that no longer match.
      - `warnings` string[]: Non-fatal notices from the evaluation, surfaced to telemetry and debugging tools.
      - `metadata` object: Rule-authored metadata. Free-form and read by NOTHING — no key here is interpreted. A rule decides WHICH products are recommended; it never prices them. The discount and the struck-through price belong to the placement the rule is attached to, where the merchant sets them.
    - `telemetry` object, required: Telemetry facts for one rule evaluation. Emission is the broker's job; the shape is typed here so the vocabulary is pinned with the envelope. Errors and timeouts collapse to empty behaviorally (the surface follows the placement's onEmpty policy) but stay distinct here.
      - `outcome` 'result' | 'empty' | 'error', required: 'result' = items returned; 'empty' = valid miss (zero items); 'error' = the evaluation failed or timed out and the surface fell back to empty behavior.
      - `errorClass` string: Free-form classifier of the failure when outcome is 'error'. Deliberately not an enum yet — the vocabulary gets pinned once real failure modes exist.
      - `datasetRevisions` object[]: The dataset revisions the evaluation's pinned reads resolved to, one entry per distinct key read. This is the field the impression stamp records (persistence lands with the impression store, M4).
        - `key` string, required: The dataset key the evaluation read.
        - `revision` integer, required: The revision the evaluation's pinned read resolved to.
      - `removedByExclusion` object[]: Items removed by serving exclusions, with only the item id and the approved removal reason.
        - `itemId` string, required
        - `reason` 'product_id' | 'tag' | 'collection' | 'vendor' | 'metadata_unavailable', required
      - `attempts` object[]: Per-attempt chain records in execution order, captured by the SDK runtime's accumulator — never authored by rule code. Fallback rate = attempt 1 contributed 0. Absent means no chain ran, never 'zero attempts'.
        - `base` string, required: The attempt's stable base label (e.g. 'colour-match').
        - `instance` string: Per-invocation instance qualifier (e.g. the seed product handle) for readout grouping. Present ONLY when the attempt supplied one via `attempt(base, fn, { instance })`; the key is omitted entirely otherwise, so consumers must treat its absence as normal and must never assert deep equality against a record that assumes it.
        - `contributed` integer, required: How many of this attempt's items made the final result. Non-negative integer.
        - `outcome` 'contributed' | 'empty' | 'not-selected' | 'skipped' | 'error', required: 'contributed' | 'empty' | 'not-selected' | 'skipped' | 'error' — see AgenticLogicAttemptOutcomeList for exact semantics.
      - `evaluationSource` 'serving' | 'api' | 'preview': Which lane produced this evaluation: 'serving' = a shopper-facing surface, 'api' = the metered external/MCP evaluate endpoint, 'preview' = authoring preview. 'api' and 'preview' evaluations are never impressions — readouts must exclude them.
      - `executionTier` 'template' | 'custom': Which executor ran the rule: 'template' = the shared warm template runner, 'custom' = the org's own rule Lambda. Absent when the evaluation exited before an assignment resolved, so nothing ran. Not the same vocabulary as the stored version execution kind.
      - `deadlineMs` integer: The RELATIVE time budget the evaluation ran under, in milliseconds — min(surface hard cap, the assignment's budgetMs). The absolute epoch deadline is meaningless in a readout and already rides the invocation as system.deadlineAtMs. Absent when the evaluation exited before an assignment resolved.
      - `fallback` 'not-applicable' | 'hidden' | 'materialized' | 'unavailable' | 'failed': What the placement's onEmpty policy did with an empty rule result: 'not-applicable' = the rule returned items, so no policy ran; 'hidden' = onEmpty is 'hide'; 'materialized' = the smart-recs fallback supplied the items; 'unavailable' = smart-recs was asked and had nothing to give; 'failed' = the fallback read itself errored. This is what separates empties a readout would otherwise collapse into one bucket.
      - `classifierBreakerTripped` boolean: Whether the org-scoped storefront-classifier breaker was already open when this evaluation started, so the rule was short-circuited rather than executed. Absent when the evaluation exited before the breaker was consulted.
  - `exclusions` object
    - `evaluated` boolean, required
    - `removals` object[], required
      - `itemId` string, required
      - `reason` 'product_id' | 'tag' | 'collection' | 'vendor' | 'metadata_unavailable', required
      - `matchedValue` string, nullable, required
  - `coverage` object, nullable, required: The join between one evaluation's branch-level telemetry and the rule's graph artifact: which branch fired, the state of every branch, and the solid path the renderer keeps undimmed.
    - `firedBranchId` string, nullable, required: The branch node id the evaluation's branchFired telemetry names, or null when nothing matched (or the reported base names no branch node in this graph revision).
    - `branchStates` object, required: Per branch node id: 'fired' | 'attempted' (tried but did not fire) | 'skipped' (never reached).
    - `traversedNodeIds` string[], required: Node ids on the evaluation's solid path; the renderer dims everything else.
    - `terminalNodeId` string, required: The 'output' node when the envelope carries items, the 'fallback-terminal' when it is empty.
  - `hydratedItems` object[], required
    - `id` string, required
    - `variantId` string, nullable, required
    - `title` string, required
    - `variantTitle` string, nullable
    - `price` string, required
  - `onEmpty` 'hide' | 'smart-recs'
  - `evaluated` object, required
    - `source` 'draft' | 'published_version' | 'template', required
    - `version` integer, nullable, required
  - `logs` object[]: Entries the rule's `logger` calls wrote during this run, in call order. Present for code rules, including when the rule threw or exceeded its budget; absent for builder rules and when the run failed on the platform side (for example a failed SDK read), including a throw after the rule caught a failed SDK read; a budget overrun after one still has them. When a failed run's output exceeds 256KB, only its earliest and latest entries are kept and `logsTruncated` is true. Each `timestamp` is host wall-clock time when `logger` was called, with millisecond precision, so the difference between two entries is the elapsed time between those calls.
    - `level` 'log' | 'info' | 'warn' | 'error' | 'debug', required: Log level ('log' | 'info' | 'warn' | 'error' | 'debug').
    - `message` string, required: The log message.
    - `details` unknown
    - `invocationId` string, uuid: Broker invocation identifier that correlates this entry with one recorded rule run.
    - `timestamp` string, date-time, required: When the entry was emitted (ISO-8601 UTC).
  - `logsTruncated` true: True when the failed run wrote more than 256KB of output, so `logs` holds only its earliest and latest entries. Absent when `logs` is complete.

## Other responses

- `429`: Too Many Requests

---

[API](https://skmtc.dev/intelligems/apis/intelligems-external-api-v25-10-beta.md) - [All operations](https://skmtc.dev/intelligems/apis/intelligems-external-api-v25-10-beta/llms.txt) - [OpenAPI document](https://skmtc.dev/intelligems/apis/intelligems-external-api-v25-10-beta/revisions/7b769b2f90b1?raw)
