{
  "openapi": "3.1.0",
  "info": {
    "title": "AfterSell Strategies API",
    "version": "1.0.0",
    "description": "Evaluate a strategy against provided context to return the desired experience."
  },
  "servers": [
    {
      "url": "https://start.aftersell.app"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Strategy-Api-Key"
      }
    },
    "schemas": {
      "Metafield": {
        "type": "object",
        "required": [
          "namespace",
          "key",
          "value"
        ],
        "properties": {
          "namespace": {
            "type": "string",
            "description": "Metafield namespace"
          },
          "key": {
            "type": "string",
            "description": "Metafield key"
          },
          "value": {
            "type": "string",
            "description": "Metafield value"
          }
        }
      },
      "ContextProduct": {
        "type": "object",
        "required": [
          "productId"
        ],
        "properties": {
          "productId": {
            "type": "string",
            "description": "Shopify GID format: `gid://shopify/Product/{id}`"
          },
          "variantId": {
            "type": "string",
            "description": "Shopify GID format: `gid://shopify/ProductVariant/{id}`"
          },
          "quantity": {
            "type": "number",
            "description": "Item quantity (non-negative integer)",
            "minimum": 0
          },
          "price": {
            "type": "number",
            "description": "Unit price in dollars (non-negative)",
            "minimum": 0
          },
          "handle": {
            "type": "string",
            "description": "Product handle/slug"
          },
          "title": {
            "type": "string",
            "description": "Product title"
          },
          "vendor": {
            "type": "string",
            "description": "Product vendor name"
          },
          "productType": {
            "type": "string",
            "description": "Product type"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Product tags"
          },
          "collections": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Collection GIDs the product belongs to"
          },
          "sellingPlan": {
            "type": "string",
            "enum": [
              "subscription",
              "one-time"
            ],
            "description": "Selling plan type"
          },
          "metafields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Metafield"
            },
            "description": "Product metafields for predicate matching. Used for evaluation only — never returned in the response."
          }
        }
      },
      "CartContext": {
        "type": "object",
        "properties": {
          "subtotal": {
            "type": "number",
            "description": "Cart subtotal in store-currency major units (e.g. dollars), consistent with product `price`. The value is compared verbatim against `cart_subtotal` rule thresholds — no unit conversion is applied — so send the same units you author your rules in.",
            "minimum": 0
          },
          "itemCount": {
            "type": "number",
            "description": "Total item quantity across all lines",
            "minimum": 0
          },
          "lineCount": {
            "type": "number",
            "description": "Number of distinct line items",
            "minimum": 0
          },
          "attributes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Cart attributes as a key-value map"
          },
          "notes": {
            "type": "string",
            "description": "Cart note"
          }
        }
      },
      "CustomerContext": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "description": "Shopify Customer GID"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Customer tags"
          },
          "countryCode": {
            "type": "string",
            "description": "Customer billing country code"
          },
          "provinceCode": {
            "type": "string",
            "description": "Customer billing province code"
          },
          "locale": {
            "type": "string",
            "description": "Customer locale"
          },
          "orderCount": {
            "type": "number",
            "description": "Number of orders placed",
            "minimum": 0
          },
          "totalSpent": {
            "type": "number",
            "description": "Lifetime spend in store currency",
            "minimum": 0
          },
          "acceptsMarketing": {
            "type": "boolean",
            "description": "Whether the customer accepts marketing"
          }
        }
      },
      "SessionContext": {
        "type": "object",
        "properties": {
          "utmSource": {
            "type": "string",
            "description": "UTM source parameter"
          },
          "utmMedium": {
            "type": "string",
            "description": "UTM medium parameter"
          },
          "utmCampaign": {
            "type": "string",
            "description": "UTM campaign parameter"
          },
          "utmContent": {
            "type": "string",
            "description": "UTM content parameter"
          },
          "utmTerm": {
            "type": "string",
            "description": "UTM term parameter"
          },
          "currencyCode": {
            "type": "string",
            "description": "Store currency code"
          },
          "shippingCountryCode": {
            "type": "string",
            "description": "Shipping destination country code"
          },
          "shippingProvinceCode": {
            "type": "string",
            "description": "Shipping destination province code"
          },
          "shippingMethod": {
            "type": "string",
            "description": "Selected shipping method name"
          }
        }
      },
      "EvaluateRequest": {
        "type": "object",
        "required": [
          "shopDomain",
          "strategyId",
          "context"
        ],
        "properties": {
          "shopDomain": {
            "type": "string",
            "description": "Your store's permanent domain"
          },
          "strategyId": {
            "type": "string",
            "description": "The strategy ID to evaluate (a single 24-char hex MongoDB ObjectId)"
          },
          "context": {
            "type": "object",
            "properties": {
              "products": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ContextProduct"
                },
                "description": "Products to evaluate the strategy against. Optional — omit or pass `[]` to evaluate context-only rules (e.g. cart, customer, session, datetime predicates) such as a curated collection for an empty cart. Product-scoped predicates fall through to `false` when no products are supplied."
              },
              "cartToken": {
                "type": "string",
                "description": "Shopify cart token. Either `cartToken` or `checkoutToken` must be provided."
              },
              "checkoutToken": {
                "type": "string",
                "description": "Shopify checkout token. Either `cartToken` or `checkoutToken` must be provided."
              },
              "sessionId": {
                "type": "string",
                "description": "Optional session identifier for tracking and tracing"
              },
              "cart": {
                "$ref": "#/components/schemas/CartContext"
              },
              "customer": {
                "$ref": "#/components/schemas/CustomerContext"
              },
              "session": {
                "$ref": "#/components/schemas/SessionContext"
              }
            }
          }
        }
      },
      "ProductVariant": {
        "type": "object",
        "properties": {
          "variantId": {
            "type": "string",
            "description": "Shopify ProductVariant GID"
          },
          "title": {
            "type": "string",
            "description": "Variant title"
          },
          "price": {
            "type": "string",
            "description": "Variant price as a dollar-amount string"
          },
          "compareAtPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Variant compare-at price, or null"
          },
          "availableForSale": {
            "type": "boolean",
            "description": "Whether this variant is available"
          },
          "sku": {
            "type": "string",
            "description": "Variant SKU (may be absent)"
          },
          "selectedOptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            },
            "description": "Array of option name/value pairs (may be absent)"
          }
        }
      },
      "ProductImage": {
        "type": "object",
        "properties": {
          "src": {
            "type": "string",
            "description": "Image URL"
          },
          "altText": {
            "type": "string",
            "description": "Image alt text (may be absent)"
          },
          "position": {
            "type": "number",
            "description": "Image sort position"
          }
        }
      },
      "RecommendedProduct": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "description": "Shopify Product GID"
          },
          "handle": {
            "type": "string",
            "description": "Product handle/slug"
          },
          "title": {
            "type": "string",
            "description": "Product title"
          },
          "vendor": {
            "type": "string",
            "description": "Vendor name"
          },
          "productType": {
            "type": "string",
            "description": "Product type"
          },
          "price": {
            "type": "string",
            "description": "Lowest variant price as a dollar-amount string"
          },
          "compareAtPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Compare-at price, or null if not set"
          },
          "availableForSale": {
            "type": "boolean",
            "description": "Whether the product is available for purchase"
          },
          "description": {
            "type": "string",
            "description": "Product description"
          },
          "url": {
            "type": "string",
            "description": "Relative product URL"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "collectionIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductImage"
            }
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariant"
            }
          }
        }
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Error code"
          },
          "message": {
            "type": "string",
            "description": "Human-readable error description"
          }
        }
      },
      "EvaluateResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the evaluation succeeded"
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecommendedProduct"
            },
            "description": "Fully enriched recommended products"
          },
          "evaluationId": {
            "type": "string",
            "description": "Unique identifier for this evaluation. Echo it back as a line item property (e.g. `__as_offer_id`) when a shopper adds a recommended product, so the resulting order attributes back to this specific evaluation in AfterSell reporting. Present on successful evaluations; absent on error responses that short-circuit before evaluation."
          },
          "resolution": {
            "type": "object",
            "properties": {
              "strategyId": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The strategy that produced the result. `null` on any error response, and on a successful evaluation where no strategy matched the requested ID for this shop; otherwise echoes the requested `strategyId`."
              },
              "matchedRuleIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "IDs of rules that matched"
              },
              "selectionMethod": {
                "type": "string",
                "description": "Always `rules_engine`"
              },
              "fallbackUsed": {
                "type": "boolean",
                "description": "Whether fallback logic was used"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "servedFromCache": {
                "type": "boolean",
                "description": "Whether product data was served from cache"
              },
              "processingTimeMs": {
                "type": "number",
                "description": "Server-side processing time in milliseconds"
              },
              "data": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                },
                "description": "Key-value pairs emitted by matched rules with `key_value` outputs. Values are always strings. Always present; may be an empty object. Keys from the first matching rule win."
              },
              "explanation": {
                "type": "string",
                "description": "Optional debug explanation (may be absent)"
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Present only when errors occur"
          }
        }
      }
    }
  },
  "paths": {
    "/api/public/strategy/evaluate": {
      "post": {
        "operationId": "evaluateStrategy",
        "summary": "Evaluate Strategy",
        "description": "Evaluate a strategy against provided context to return the desired experience.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvaluateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Evaluation successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing required fields, invalid ObjectId format, or neither `cartToken` nor `checkoutToken` provided",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                },
                "examples": {
                  "VALIDATION_ERROR": {
                    "summary": "Validation error",
                    "value": {
                      "success": false,
                      "products": [],
                      "resolution": {
                        "strategyId": null,
                        "matchedRuleIds": [],
                        "selectionMethod": "rules_engine",
                        "fallbackUsed": false
                      },
                      "meta": {
                        "servedFromCache": false,
                        "processingTimeMs": 0,
                        "data": {}
                      },
                      "errors": [
                        {
                          "code": "VALIDATION_ERROR",
                          "message": "Validation failed"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication error — missing or invalid API key, key not configured for shop, or unrecognized shop domain. No further detail is returned in the response body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                },
                "examples": {
                  "AUTH_ERROR": {
                    "summary": "Authentication error",
                    "value": {
                      "success": false,
                      "products": [],
                      "resolution": {
                        "strategyId": null,
                        "matchedRuleIds": [],
                        "selectionMethod": "rules_engine",
                        "fallbackUsed": false
                      },
                      "meta": {
                        "servedFromCache": false,
                        "processingTimeMs": 0,
                        "data": {}
                      },
                      "errors": [
                        {
                          "code": "AUTH_ERROR",
                          "message": "Unauthorized"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authorization error — Strategy is not enabled for this shop.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                },
                "examples": {
                  "FORBIDDEN": {
                    "summary": "Strategies not enabled for this shop",
                    "value": {
                      "success": false,
                      "products": [],
                      "resolution": {
                        "strategyId": null,
                        "matchedRuleIds": [],
                        "selectionMethod": "rules_engine",
                        "fallbackUsed": false
                      },
                      "meta": {
                        "servedFromCache": false,
                        "processingTimeMs": 0,
                        "data": {}
                      },
                      "errors": [
                        {
                          "code": "FORBIDDEN",
                          "message": "Strategy is not enabled for this shop"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Server error — shop configuration issue or unexpected internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                },
                "examples": {
                  "CONFIG_ERROR": {
                    "summary": "Shop configuration error",
                    "value": {
                      "success": false,
                      "products": [],
                      "resolution": {
                        "strategyId": null,
                        "matchedRuleIds": [],
                        "selectionMethod": "rules_engine",
                        "fallbackUsed": false
                      },
                      "meta": {
                        "servedFromCache": false,
                        "processingTimeMs": 0,
                        "data": {}
                      },
                      "errors": [
                        {
                          "code": "CONFIG_ERROR",
                          "message": "Shop configuration error"
                        }
                      ]
                    }
                  },
                  "INTERNAL_ERROR": {
                    "summary": "Unexpected internal error",
                    "value": {
                      "success": false,
                      "products": [],
                      "resolution": {
                        "strategyId": null,
                        "matchedRuleIds": [],
                        "selectionMethod": "rules_engine",
                        "fallbackUsed": false
                      },
                      "meta": {
                        "servedFromCache": false,
                        "processingTimeMs": 0,
                        "data": {}
                      },
                      "errors": [
                        {
                          "code": "INTERNAL_ERROR",
                          "message": "Failed to evaluate strategy rules"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}