openapi: 3.1.0
info:
  title: makeup.land API
  version: 1.0.0
  summary: REST API for makeup.land — Hebrew-RTL professional cosmetics storefront with bilingual product data, ILS + ℳ-credit dual-tender pricing, and agent-friendly endpoints.
  description: |-
    All endpoints live under `/api/v1/`. Bearer tokens are issued from the admin panel and carry a scope (`full` / `register` / `giftcards` / `proposals`) plus an optional `read_only` flag. Phone-keyed endpoints (cart, gift cards list, payment links, best deals) accept a phone in the query string instead. The public `/gift-cards/validate` endpoint is the only fully unauthenticated route.

    Catalog is Hebrew-primary (he-IL); monetary values that end in `_cents` are integer agorot (100 = ₪1). Products can also be priceable in ℳ-credits — see `credit_price` on variants.

    ## Versioning & deprecation policy

    The current major version is `v1`, exposed under `/api/v1/`. Breaking changes always land under a new major version path (`/api/v2/`); additive changes (new optional fields, new endpoints, new error_code values) ship in-place on `v1` without notice.

    When an endpoint or field is scheduled for removal, responses include the `Deprecation: true` and `Sunset: <RFC 7231 HTTP-date>` headers (per RFC 8594 / RFC 9745) for at least 6 months before the sunset date, with a `Link: <docs>; rel="deprecation"` header pointing at the migration guide. No V1 endpoint is currently deprecated.

    ## Error handling

    Every error response carries `{ error: <human-readable>, error_code: <enum> }` plus situational extras (e.g. `available`, `requested` on `409 insufficient_stock`; `row_errors` on `422 validation_failed`). `error_code` is the stable contract — agents should branch on it. The `error` string may shift in copy or language.

    ## Idempotency

    Mutation endpoints (POST/PATCH/DELETE) accept an `Idempotency-Key` request header. Retries with the same key inside a 24h window are guaranteed to be either no-ops or replays of the original response, never duplicate effects. Recommended pattern: a UUIDv4 generated by the caller per logical operation.
  contact:
    name: makeup.land
    url: https://makeup.land
  license:
    name: Proprietary
    url: https://makeup.land/terms-of-service
servers:
  - url: https://makeup.land
    description: Production
tags:
  - name: Products
    description: Catalog browsing + semantic search
  - name: Brands
    description: Brand directory
  - name: Customers
    description: Customer CRUD + tags + opportunities + best deals
  - name: Cart
    description: Phone-keyed cart read/write (ILS + ℳ-credits)
  - name: Orders
    description: Customer order history
  - name: Gift Cards
    description: Recipient cards, validation, redemption
  - name: Payment Links
    description: Outstanding payment requests
  - name: Register
    description: External registration intake + audit
  - name: Proposals
    description: Catalog enrichment proposal intake
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ml_<hex24>
      description: API bearer token issued under `api_tokens`. Prefix `ml_` is required. Per-token scope and `read_only` flag govern endpoint + write access.
    phoneIdentifier:
      type: apiKey
      in: query
      name: phone
      description: Phone number in E.164 format (e.g. `+972501234567`) that selects which customer's resources to return. **NOT a credential** — endpoints that accept this also REQUIRE `bearerAuth`. The bearer authenticates the calling partner; the phone selects the customer. For POST/PATCH cart-mutation endpoints, the phone goes in the JSON body instead of the query string.
  schemas: {}
x-scopes:
  full: Full read + write access. Default scope for first-party tokens.
  register: Issue new customer registrations and read registrations belonging to the token's `registration_source`. Restricted to the `/register` and `/registrations` endpoints.
  giftcards: Redeem gift cards. Required only by `POST /gift-cards/redeem`. The public `/gift-cards/validate` endpoint requires no token.
  proposals: Submit catalog enrichment proposals to `/proposals`. Read-only against the rest of the catalog.
  read_only: Marker for tokens whose `read_only=true` flag rejects every write. Not negotiated at request time — set at token issuance.
paths:
  /api/v1/brands:
    get:
      operationId: listBrands
      summary: List all brands with product counts
      description: Returns every brand surfaced on the storefront, sorted server-side. Use the `slug` value to look up products via `/api/v1/products?brand=<slug>`.
      tags:
        - Brands
      security:
        - bearerAuth:
            - full
      responses:
        "200":
          description: Brand directory
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  brands:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                          description: English brand name
                        name_he:
                          anyOf:
                            - type: string
                            - type: "null"
                          description: Hebrew brand name
                        slug:
                          type: string
                          description: URL-safe slug, e.g. `bali-body`
                        logo:
                          anyOf:
                            - type: string
                            - type: "null"
                          description: Logo URL or null
                        product_count:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                      required:
                        - name
                        - name_he
                        - slug
                        - logo
                        - product_count
                      additionalProperties: false
                required:
                  - brands
                additionalProperties: false
        "401":
          description: Missing or invalid bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/products:
    get:
      operationId: listProducts
      summary: Browse / search the catalog
      description: |-
        The primary catalog endpoint. Supports full-text + semantic search, brand/tag filtering, ΔE-ranked shade matching, per-customer reward projection, and dual-tender pricing (ILS + ℳ-credits).

        **Auth tiers**
        - Tag-only or no-arg listing: bearer token optional.
        - `q` / `phone` / `include=inventory` / `relevant_to_phone`: full-scope   bearer token required.

        **Precedence**: `near_hex` ranking dominates `sort`; `sort` dominates the implicit relevance ranking.
      tags:
        - Products
      security:
        - bearerAuth:
            - full
        - {}
      parameters:
        - name: q
          in: query
          required: false
          description: Free-text query. Triggers semantic + lexical search (union, rerank). Requires a bearer token when supplied without `tag`.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Free-text query. Triggers semantic + lexical search (union, rerank). Requires a bearer token when supplied without `tag`.
            type: string
        - name: tag
          in: query
          required: false
          description: Tag filter. Token-less requests must supply this or `brand`.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Tag filter. Token-less requests must supply this or `brand`.
            type: string
        - name: brand
          in: query
          required: false
          description: Brand identifier. Matches name / slug / Hebrew name with whitespace and punctuation normalised — `Bali Body`, `balibody`, `bali-body` all collapse.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Brand identifier. Matches name / slug / Hebrew name with whitespace and punctuation normalised — `Bali Body`, `balibody`, `bali-body` all collapse.
            type: string
        - name: phone
          in: query
          required: false
          description: When supplied, the response embeds the customer's reward projection (`reward_earned` per product). Requires full-scope bearer token.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: When supplied, the response embeds the customer's reward projection (`reward_earned` per product). Requires full-scope bearer token.
            type: string
            pattern: ^\+\d{6,15}$
        - name: in_stock
          in: query
          required: false
          description: String boolean. `true` filters to in-stock variants only.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: String boolean. `true` filters to in-stock variants only.
            type: string
            enum:
              - "true"
              - "false"
        - name: max_price
          in: query
          required: false
          description: Max ILS price filter (decimal, e.g. `99.99`)
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Max ILS price filter (decimal, e.g. `99.99`)
            type: number
            minimum: 0
        - name: max_credit_cents
          in: query
          required: false
          description: Max ℳ-credit price filter in agorot
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Max ℳ-credit price filter in agorot
            type: integer
            minimum: 0
            maximum: 9007199254740991
        - name: relevant_to_phone
          in: query
          required: false
          description: Re-rank results by relevance to this customer's past orders + tags. Requires full-scope bearer token.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Re-rank results by relevance to this customer's past orders + tags. Requires full-scope bearer token.
            type: string
            pattern: ^\+\d{6,15}$
        - name: include
          in: query
          required: false
          description: Comma-separated extra-field bundles. `inventory` adds `stock_by_location` and per-variant `available`. Requires full scope.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Comma-separated extra-field bundles. `inventory` adds `stock_by_location` and per-variant `available`. Requires full scope.
            type: string
        - name: limit
          in: query
          required: false
          description: Default 10. Capped at 50.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Default 10. Capped at 50.
            type: integer
            minimum: 1
            maximum: 50
        - name: page
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: integer
            minimum: 1
            maximum: 9007199254740991
        - name: near_hex
          in: query
          required: false
          description: Single `#RRGGBB` hex or comma-separated list (≤8). Returns products ranked by ΔE distance to the closest match.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Single `#RRGGBB` hex or comma-separated list (≤8). Returns products ranked by ΔE distance to the closest match.
            type: string
        - name: delta_e_max
          in: query
          required: false
          description: ΔE2000 cutoff used with `near_hex`. Default 80 single-hex, 200 multi-hex.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: ΔE2000 cutoff used with `near_hex`. Default 80 single-hex, 200 multi-hex.
            type: number
            minimum: 0
        - name: hue_family
          in: query
          required: false
          description: Comma-separated hue families. Post-filter — narrows results to products whose swatches fall in any of the listed families.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Comma-separated hue families. Post-filter — narrows results to products whose swatches fall in any of the listed families.
            type: string
        - name: sort
          in: query
          required: false
          description: Default `relevance`. `rating` uses a Bayesian shrinkage to avoid single-review products winning.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Default `relevance`. `rating` uses a Bayesian shrinkage to avoid single-review products winning.
            type: string
            enum:
              - price_asc
              - price_desc
              - popularity
              - rating
              - relevance
        - name: coverage
          in: query
          required: false
          description: How multi-value filters (tags, hue_family) combine. `all` (default) intersects; `any` unions.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: How multi-value filters (tags, hue_family) combine. `all` (default) intersects; `any` unions.
            type: string
            enum:
              - all
              - any
      responses:
        "200":
          description: Paginated catalog page
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  products:
                    type: array
                    items:
                      type: object
                      properties:
                        product_id:
                          type: string
                          minLength: 1
                        title:
                          type: string
                        handle:
                          type: string
                          description: URL-safe slug
                        url:
                          type: string
                          format: uri
                        image_url:
                          anyOf:
                            - type: string
                              format: uri
                            - type: "null"
                        images:
                          type: array
                          items:
                            type: string
                            format: uri
                        price:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        compare_at_price:
                          anyOf:
                            - type: number
                              minimum: 0
                              description: Amount in ILS as a decimal number (e.g. 12.50)
                            - type: "null"
                        credit_price:
                          anyOf:
                            - type: integer
                              minimum: 0
                              maximum: 9007199254740991
                              description: Amount in integer agorot (100 = ₪1)
                            - type: "null"
                        min_price:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        max_price:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        brand:
                          anyOf:
                            - type: object
                              properties:
                                slug:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                                name:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                              additionalProperties: false
                            - type: "null"
                        product_type:
                          anyOf:
                            - type: string
                            - type: "null"
                        in_stock:
                          type: boolean
                        description:
                          anyOf:
                            - type: string
                            - type: "null"
                        tags:
                          type: array
                          items:
                            type: string
                        variants:
                          type: array
                          items:
                            type: object
                            properties:
                              variant_id:
                                type: string
                                minLength: 1
                              title:
                                anyOf:
                                  - type: string
                                  - type: "null"
                              price:
                                type: number
                                minimum: 0
                                description: Amount in ILS as a decimal number (e.g. 12.50)
                              compare_at_price:
                                anyOf:
                                  - type: number
                                    minimum: 0
                                    description: Amount in ILS as a decimal number (e.g. 12.50)
                                  - type: "null"
                              credit_price:
                                anyOf:
                                  - type: integer
                                    minimum: 0
                                    maximum: 9007199254740991
                                    description: Amount in integer agorot (100 = ₪1)
                                  - type: "null"
                                description: ℳ-credit price in agorot. `null` for ILS-only variants.
                              in_stock:
                                type: boolean
                              image_url:
                                anyOf:
                                  - type: string
                                    format: uri
                                  - type: "null"
                              swatch:
                                type: object
                                properties:
                                  hex:
                                    type: string
                                    pattern: ^#[0-9a-fA-F]{6}$
                                    description: Swatch color
                                  variant_id:
                                    anyOf:
                                      - type: string
                                        minLength: 1
                                      - type: "null"
                                  option_handle:
                                    anyOf:
                                      - type: string
                                      - type: "null"
                                required:
                                  - hex
                                  - variant_id
                                  - option_handle
                                additionalProperties: false
                              inventory_policy:
                                type: string
                                enum:
                                  - deny
                                  - continue
                              available:
                                type: integer
                                minimum: -9007199254740991
                                maximum: 9007199254740991
                              stock_by_location:
                                type: object
                                propertyNames:
                                  type: string
                                additionalProperties:
                                  type: integer
                                  minimum: 0
                                  maximum: 9007199254740991
                                description: Optional per-location stock breakdown when `include=inventory`
                            required:
                              - variant_id
                              - title
                              - price
                              - compare_at_price
                              - credit_price
                              - in_stock
                              - image_url
                            additionalProperties: false
                        swatches:
                          type: array
                          items:
                            type: object
                            properties:
                              hex:
                                type: string
                                pattern: ^#[0-9a-fA-F]{6}$
                                description: Swatch color
                              variant_id:
                                anyOf:
                                  - type: string
                                    minLength: 1
                                  - type: "null"
                              option_handle:
                                anyOf:
                                  - type: string
                                  - type: "null"
                            required:
                              - hex
                              - variant_id
                              - option_handle
                            additionalProperties: false
                        average_rating:
                          anyOf:
                            - type: number
                              minimum: 0
                              maximum: 5
                            - type: "null"
                        total_reviews:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                        reward_earned:
                          description: Per-customer projected reward in agorot. Present when `phone` supplied.
                          anyOf:
                            - type: integer
                              minimum: 0
                              maximum: 9007199254740991
                              description: Amount in integer agorot (100 = ₪1)
                            - type: "null"
                        inventory_policy:
                          type: string
                          enum:
                            - deny
                            - continue
                        track_quantity:
                          type: boolean
                        stock_by_location:
                          type: object
                          propertyNames:
                            type: string
                          additionalProperties:
                            type: integer
                            minimum: 0
                            maximum: 9007199254740991
                          description: Optional per-location stock breakdown when `include=inventory`
                      required:
                        - product_id
                        - title
                        - handle
                        - url
                        - image_url
                        - images
                        - price
                        - compare_at_price
                        - credit_price
                        - min_price
                        - max_price
                        - brand
                        - product_type
                        - in_stock
                        - description
                        - tags
                        - variants
                        - swatches
                        - average_rating
                        - total_reviews
                      additionalProperties: false
                  total:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                  page:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  has_more:
                    type: boolean
                  partial:
                    type: boolean
                    description: True when post-filters (`hue_family`, etc.) underfilled the page — the catalog page below this one may contain more matches.
                  customer:
                    description: Echoed back when the `phone` param resolved to a customer.
                    type: object
                    properties:
                      name:
                        anyOf:
                          - type: string
                          - type: "null"
                      tags:
                        type: array
                        items:
                          type: string
                      credit_balance:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: Amount in integer agorot (100 = ₪1)
                      credit_balance_formatted:
                        type: string
                    required:
                      - name
                      - tags
                      - credit_balance
                      - credit_balance_formatted
                    additionalProperties: false
                required:
                  - products
                  - total
                  - page
                  - has_more
                  - partial
                additionalProperties: false
        "400":
          description: Invalid hex, missing required filter, or invalid parameter
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Auth required for the requested fields but not supplied
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/customers:
    get:
      operationId: getCustomer
      summary: Look up a customer by phone or email
      description: "Exactly one of `phone` (E.164) or `email` must be supplied. Returns `customer: null` (200) when no match — the endpoint never 404s on lookup miss so polling clients can treat the response as a stable envelope."
      tags:
        - Customers
      security:
        - bearerAuth:
            - full
      parameters:
        - name: phone
          in: query
          required: false
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
        - name: email
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            format: email
            pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
      responses:
        "200":
          description: Customer or null
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  customer:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            minLength: 1
                          name:
                            anyOf:
                              - type: string
                              - type: "null"
                          first_name:
                            anyOf:
                              - type: string
                              - type: "null"
                          last_name:
                            anyOf:
                              - type: string
                              - type: "null"
                          email:
                            anyOf:
                              - type: string
                              - type: "null"
                          phone:
                            anyOf:
                              - type: string
                              - type: "null"
                          avatar_url:
                            anyOf:
                              - type: string
                                format: uri
                              - type: "null"
                          tags:
                            type: array
                            items:
                              type: string
                          city:
                            anyOf:
                              - type: string
                              - type: "null"
                          address:
                            anyOf:
                              - type: string
                              - type: "null"
                          total_spent:
                            type: number
                            minimum: 0
                            description: Amount in ILS as a decimal number (e.g. 12.50)
                          total_orders:
                            type: integer
                            minimum: 0
                            maximum: 9007199254740991
                          created_at:
                            type: string
                            description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                          last_activity_at:
                            anyOf:
                              - type: string
                                description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                              - type: "null"
                          credit_balance:
                            type: integer
                            minimum: 0
                            maximum: 9007199254740991
                            description: ℳ-credit wallet balance in agorot
                          credit_balance_formatted:
                            type: string
                            description: e.g. `ℳ12.50`
                        required:
                          - id
                          - name
                          - first_name
                          - last_name
                          - email
                          - phone
                          - avatar_url
                          - tags
                          - city
                          - address
                          - total_spent
                          - total_orders
                          - created_at
                          - last_activity_at
                          - credit_balance
                          - credit_balance_formatted
                        additionalProperties: false
                      - type: "null"
                required:
                  - customer
                additionalProperties: false
        "400":
          description: Missing identifier or invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Scope mismatch
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
    post:
      operationId: upsertCustomer
      summary: Create or update a customer (bulk-import friendly)
      description: Creates the customer if missing, otherwise patches the existing row. Tag operations are additive — submitted tags are merged with the existing set; no tag removal happens here. Deliberately does NOT fire the welcome-bonus path; use `/api/v1/register` for trusted onboarding.
      tags:
        - Customers
      security:
        - bearerAuth:
            - full
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                phone:
                  type: string
                  pattern: ^\+\d{6,15}$
                  description: Phone number in E.164 format, e.g. +972501234567
                name:
                  description: Required when creating a new customer
                  type: string
                email:
                  type: string
                  format: email
                  pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
                tags:
                  type: array
                  items:
                    type: string
              required:
                - phone
              additionalProperties: false
      responses:
        "200":
          description: Updated existing customer
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  customer:
                    type: object
                    properties:
                      id:
                        type: string
                        minLength: 1
                      name:
                        anyOf:
                          - type: string
                          - type: "null"
                      first_name:
                        anyOf:
                          - type: string
                          - type: "null"
                      last_name:
                        anyOf:
                          - type: string
                          - type: "null"
                      email:
                        anyOf:
                          - type: string
                          - type: "null"
                      phone:
                        anyOf:
                          - type: string
                          - type: "null"
                      avatar_url:
                        anyOf:
                          - type: string
                            format: uri
                          - type: "null"
                      tags:
                        type: array
                        items:
                          type: string
                      city:
                        anyOf:
                          - type: string
                          - type: "null"
                      address:
                        anyOf:
                          - type: string
                          - type: "null"
                      total_spent:
                        type: number
                        minimum: 0
                        description: Amount in ILS as a decimal number (e.g. 12.50)
                      total_orders:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      created_at:
                        type: string
                        description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      last_activity_at:
                        anyOf:
                          - type: string
                            description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                          - type: "null"
                      credit_balance:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: ℳ-credit wallet balance in agorot
                      credit_balance_formatted:
                        type: string
                        description: e.g. `ℳ12.50`
                    required:
                      - id
                      - name
                      - first_name
                      - last_name
                      - email
                      - phone
                      - avatar_url
                      - tags
                      - city
                      - address
                      - total_spent
                      - total_orders
                      - created_at
                      - last_activity_at
                      - credit_balance
                      - credit_balance_formatted
                    additionalProperties: false
                  created:
                    type: boolean
                    description: "`true` when the customer was newly created"
                required:
                  - customer
                  - created
                additionalProperties: false
        "201":
          description: Created new customer
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  customer:
                    type: object
                    properties:
                      id:
                        type: string
                        minLength: 1
                      name:
                        anyOf:
                          - type: string
                          - type: "null"
                      first_name:
                        anyOf:
                          - type: string
                          - type: "null"
                      last_name:
                        anyOf:
                          - type: string
                          - type: "null"
                      email:
                        anyOf:
                          - type: string
                          - type: "null"
                      phone:
                        anyOf:
                          - type: string
                          - type: "null"
                      avatar_url:
                        anyOf:
                          - type: string
                            format: uri
                          - type: "null"
                      tags:
                        type: array
                        items:
                          type: string
                      city:
                        anyOf:
                          - type: string
                          - type: "null"
                      address:
                        anyOf:
                          - type: string
                          - type: "null"
                      total_spent:
                        type: number
                        minimum: 0
                        description: Amount in ILS as a decimal number (e.g. 12.50)
                      total_orders:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      created_at:
                        type: string
                        description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      last_activity_at:
                        anyOf:
                          - type: string
                            description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                          - type: "null"
                      credit_balance:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: ℳ-credit wallet balance in agorot
                      credit_balance_formatted:
                        type: string
                        description: e.g. `ℳ12.50`
                    required:
                      - id
                      - name
                      - first_name
                      - last_name
                      - email
                      - phone
                      - avatar_url
                      - tags
                      - city
                      - address
                      - total_spent
                      - total_orders
                      - created_at
                      - last_activity_at
                      - credit_balance
                      - credit_balance_formatted
                    additionalProperties: false
                  created:
                    type: boolean
                    description: "`true` when the customer was newly created"
                required:
                  - customer
                  - created
                additionalProperties: false
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Read-only token or scope mismatch
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "409":
          description: Phone conflict
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/customers/{id}/tags:
    patch:
      operationId: patchCustomerTags
      summary: Add / remove / replace tags on a customer
      description: "Order of application: `replace → remove → add`. Deduplicates the final set. Emits `customer.tags_changed` on actual change."
      tags:
        - Customers
      security:
        - bearerAuth:
            - full
      parameters:
        - name: id
          in: path
          required: true
          description: Customer ID
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            minLength: 1
            description: Customer ID
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                add:
                  type: array
                  items:
                    type: string
                remove:
                  type: array
                  items:
                    type: string
                replace:
                  description: Map of `old_tag → new_tag` applied before remove/add
                  type: object
                  propertyNames:
                    type: string
                  additionalProperties:
                    type: string
              additionalProperties: false
      responses:
        "200":
          description: Tags mutated
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  customer_id:
                    type: string
                    minLength: 1
                  tags:
                    type: array
                    items:
                      type: string
                  tags_added:
                    type: array
                    items:
                      type: string
                  tags_removed:
                    type: array
                    items:
                      type: string
                  tags_replaced:
                    type: object
                    propertyNames:
                      type: string
                    additionalProperties:
                      type: string
                required:
                  - customer_id
                  - tags
                  - tags_added
                  - tags_removed
                  - tags_replaced
                additionalProperties: false
        "400":
          description: No tag operations supplied
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Read-only token or scope mismatch
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/customers/{id}/opportunities:
    get:
      operationId: listCustomerOpportunities
      summary: Open M-Club opportunities + referral metadata for a customer
      description: Path param is phone in E.164 (URL-encoded) — named `id` for routing consistency with the sibling `/{id}/tags` route. Register-scoped tokens can only read opportunities for customers whose `registration_source_id` matches the token's source.
      tags:
        - Customers
      security:
        - bearerAuth:
            - full
            - register
      parameters:
        - name: id
          in: path
          required: true
          description: Customer phone, E.164 (URL-encode the `+`)
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Customer phone, E.164 (URL-encode the `+`)
      responses:
        "200":
          description: Opportunities + referral metadata
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  customer:
                    type: object
                    properties:
                      id:
                        type: string
                        minLength: 1
                      phone:
                        type: string
                      name:
                        anyOf:
                          - type: string
                          - type: "null"
                      tags:
                        type: array
                        items:
                          type: string
                      credit_balance:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: Amount in integer agorot (100 = ₪1)
                      credit_balance_formatted:
                        type: string
                    required:
                      - id
                      - phone
                      - name
                      - tags
                      - credit_balance
                      - credit_balance_formatted
                    additionalProperties: false
                  opportunities:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          minLength: 1
                        kind:
                          type: string
                          description: e.g. `welcome_bonus`, `upgrade_pro`, `referral`
                        title_he:
                          type: string
                        description_he:
                          type: string
                        bonus_formatted:
                          type: string
                          description: e.g. `ℳ50`
                        cta_action:
                          anyOf:
                            - type: string
                            - type: "null"
                        cta_url:
                          anyOf:
                            - type: string
                              format: uri
                            - type: "null"
                        context:
                          type: object
                          properties: {}
                          additionalProperties: {}
                        available_at:
                          anyOf:
                            - type: string
                              description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                            - type: "null"
                        sort_priority:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                      required:
                        - id
                        - kind
                        - title_he
                        - description_he
                        - bonus_formatted
                        - cta_action
                        - cta_url
                        - context
                        - available_at
                        - sort_priority
                      additionalProperties: false
                  referral:
                    type: object
                    properties:
                      code:
                        type: string
                      share_url_wa:
                        type: string
                        format: uri
                      level1_count:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      total_earned_from_referrals:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: Amount in integer agorot (100 = ₪1)
                    required:
                      - code
                      - share_url_wa
                      - level1_count
                      - total_earned_from_referrals
                    additionalProperties: false
                required:
                  - customer
                  - opportunities
                  - referral
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Scope mismatch or token's registration source is not linked to the customer
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "429":
          description: Rate limit exceeded (60/min per token)
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
      x-rate-limit:
        max: 60
        windowSeconds: 60
        keyedOn: token-id
  /api/v1/customers/best-deals:
    get:
      operationId: getCustomerBestDeals
      summary: Personalised best-deal candidates for a customer
      description: Computes redeem-strategy options (`all_ils` / `redeem_max` / `specials_only` / `specials_plus_redeem`) using the customer's wallet balance and tag-targeted offers. Returns curated deals ranked by `deal_score`.
      tags:
        - Customers
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: phone
          in: query
          required: true
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
      responses:
        "200":
          description: Personalised offer set
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  wallet:
                    type: object
                    properties:
                      balance_cents:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                        description: Amount in integer agorot (100 = ₪1)
                      balance_formatted:
                        type: string
                    required:
                      - balance_cents
                      - balance_formatted
                    additionalProperties: false
                  retail_total_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                  target_cash_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                  candidates:
                    type: array
                    items:
                      type: object
                      properties: {}
                      additionalProperties: {}
                  options:
                    type: array
                    items:
                      type: object
                      properties: {}
                      additionalProperties: {}
                  recommendation:
                    anyOf:
                      - type: object
                        properties:
                          option_id:
                            type: string
                          savings_vs_all_ils_cents:
                            type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                        required:
                          - option_id
                          - savings_vs_all_ils_cents
                        additionalProperties: false
                      - type: "null"
                  curated_deals:
                    type: array
                    items:
                      type: object
                      properties: {}
                      additionalProperties: {}
                  curated_has_more:
                    type: boolean
                  sources_used:
                    type: array
                    items:
                      type: string
                required:
                  - wallet
                  - retail_total_cents
                  - target_cash_cents
                  - candidates
                  - options
                  - recommendation
                  - curated_deals
                  - curated_has_more
                  - sources_used
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "429":
          description: Rate limit exceeded (60/min per IP)
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
      x-rate-limit:
        max: 60
        windowSeconds: 60
        keyedOn: source-ip
  /api/v1/cart:
    get:
      operationId: getCart
      summary: Read the current cart for a phone-keyed customer
      description: "Returns the most-recently-updated cart for the customer. Credits-mode lines do NOT contribute to `reward_projection_*` per the rewards invariant. Returns `cart_id: null` when the customer has no cart yet."
      tags:
        - Cart
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: phone
          in: query
          required: true
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
      responses:
        "200":
          description: Current cart
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  cart_id:
                    anyOf:
                      - type: string
                        minLength: 1
                      - type: "null"
                  customer_id:
                    type: string
                    minLength: 1
                  updated_at:
                    anyOf:
                      - type: string
                        description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      - type: "null"
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        line_item_id:
                          type: string
                          minLength: 1
                        product_id:
                          type: string
                          minLength: 1
                        variant_id:
                          type: string
                          minLength: 1
                        product_title:
                          type: string
                        variant_title:
                          anyOf:
                            - type: string
                            - type: "null"
                        brand_slug:
                          anyOf:
                            - type: string
                            - type: "null"
                        product_slug:
                          anyOf:
                            - type: string
                            - type: "null"
                        image:
                          anyOf:
                            - type: string
                            - type: "null"
                        price:
                          type: number
                          minimum: 0
                          description: Per-unit ILS price
                        tender:
                          type: string
                          enum:
                            - ils
                            - credits
                          description: Per-line tender. `ils` debits the order subtotal in shekels; `credits` debits the customer's ℳ-credit wallet. A variant must be priceable in the requested tender or the request is rejected with `tender_unavailable`.
                        credit_price:
                          anyOf:
                            - type: integer
                              minimum: 0
                              maximum: 9007199254740991
                              description: Amount in integer agorot (100 = ₪1)
                            - type: "null"
                          description: Per-unit ℳ-credit price in agorot, null when ILS-only
                        quantity:
                          type: integer
                          exclusiveMinimum: 0
                          maximum: 9007199254740991
                        product_tags:
                          type: array
                          items:
                            type: string
                        track_quantity:
                          type: boolean
                        gift_personalization:
                          anyOf:
                            - type: object
                              properties: {}
                              additionalProperties: {}
                            - type: "null"
                          description: Gift-message / engraving payload, structure varies by product
                        reward_projection_cents:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                          description: Projected reward earn for this line if checked out today
                      required:
                        - line_item_id
                        - product_id
                        - variant_id
                        - product_title
                        - variant_title
                        - brand_slug
                        - product_slug
                        - image
                        - price
                        - tender
                        - credit_price
                        - quantity
                        - product_tags
                        - track_quantity
                        - gift_personalization
                        - reward_projection_cents
                      additionalProperties: false
                  reward_projection_total_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                required:
                  - cart_id
                  - customer_id
                  - updated_at
                  - items
                  - reward_projection_total_cents
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
    delete:
      operationId: clearCart
      summary: Clear all carts for a customer
      description: Idempotent — deletes ALL carts (not only the most-recent one) for the phone-keyed customer. Useful after checkout or when the customer asks to start over.
      tags:
        - Cart
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: phone
          in: query
          required: true
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      responses:
        "200":
          description: Carts cleared
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  cart_id:
                    anyOf:
                      - type: string
                        minLength: 1
                      - type: "null"
                  cleared_cart_ids:
                    type: array
                    items:
                      type: string
                      minLength: 1
                required:
                  - ok
                  - cart_id
                  - cleared_cart_ids
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/cart/items:
    post:
      operationId: addCartItem
      summary: Add or increment a cart line item
      description: Upserts on `(product_id, variant_id, tender)` — same triple = quantity increments rather than creating a duplicate row. Enforces stock (skipped when `inventory_policy='continue'` or `track_quantity=false`) and, in credits-mode, a wallet-balance gate.
      tags:
        - Cart
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                phone:
                  description: Required if not supplied as query param
                  type: string
                  pattern: ^\+\d{6,15}$
                product_id:
                  type: string
                  minLength: 1
                variant_id:
                  type: string
                  minLength: 1
                quantity:
                  type: integer
                  exclusiveMinimum: 0
                  maximum: 9007199254740991
                tender:
                  description: Defaults to `ils`
                  type: string
                  enum:
                    - ils
                    - credits
                gift_personalization:
                  type: object
                  properties: {}
                  additionalProperties: {}
              required:
                - product_id
                - variant_id
                - quantity
              additionalProperties: false
      responses:
        "200":
          description: Line upserted
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  cart_id:
                    type: string
                    minLength: 1
                  line_item_id:
                    type: string
                    minLength: 1
                  quantity:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                required:
                  - ok
                  - cart_id
                  - line_item_id
                  - quantity
                additionalProperties: false
        "400":
          description: Validation error (invalid_json, invalid_quantity, variant_mismatch, tender_unavailable, product_unavailable)
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "409":
          description: Insufficient stock or insufficient credits
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/cart/items/{lineItemId}:
    patch:
      operationId: patchCartItem
      summary: Update an existing cart line item
      description: "Mutate quantity, swap variant within the same product, switch tender, or update gift personalization. Pre-gates on UNIQUE collision `(cart_id, product_id, variant_id, tender)`: a tender/variant swap that would collide with another line returns `409 line_collision` so the client can merge first."
      tags:
        - Cart
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: lineItemId
          in: path
          required: true
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            minLength: 1
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                phone:
                  type: string
                  pattern: ^\+\d{6,15}$
                  description: Phone number in E.164 format, e.g. +972501234567
                quantity:
                  type: integer
                  exclusiveMinimum: 0
                  maximum: 9007199254740991
                variant_id:
                  description: Must belong to the same product as the current line
                  type: string
                  minLength: 1
                tender:
                  type: string
                  enum:
                    - ils
                    - credits
                  description: Per-line tender. `ils` debits the order subtotal in shekels; `credits` debits the customer's ℳ-credit wallet. A variant must be priceable in the requested tender or the request is rejected with `tender_unavailable`.
                gift_personalization:
                  anyOf:
                    - type: object
                      properties: {}
                      additionalProperties: {}
                    - type: "null"
              additionalProperties: false
      responses:
        "200":
          description: Line updated
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  cart_id:
                    type: string
                    minLength: 1
                  line_item_id:
                    type: string
                    minLength: 1
                required:
                  - ok
                  - cart_id
                  - line_item_id
                additionalProperties: false
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Line item not found (or not owned)
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "409":
          description: Insufficient stock, insufficient credits, or line collision
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
    delete:
      operationId: deleteCartItem
      summary: Remove a cart line item
      description: Idempotent. Removing an unknown line item also returns 200.
      tags:
        - Cart
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: lineItemId
          in: path
          required: true
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            minLength: 1
        - name: phone
          in: query
          required: true
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      responses:
        "200":
          description: Line removed
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  cart_id:
                    type: string
                    minLength: 1
                  line_item_id:
                    type: string
                    minLength: 1
                required:
                  - ok
                  - cart_id
                  - line_item_id
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Line item not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/orders:
    get:
      operationId: listOrders
      summary: List a customer's orders
      description: Returns the most recent orders for the customer identified by `phone` (E.164) or `email`. Exactly one identifier is required. Line items include per-line tender mode and ℳ-credit debit; payment status is the read-time projected value.
      tags:
        - Orders
      security:
        - bearerAuth:
            - full
      parameters:
        - name: phone
          in: query
          required: false
          description: Customer phone, E.164. Mutually exclusive with `email`.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Customer phone, E.164. Mutually exclusive with `email`.
            type: string
            pattern: ^\+\d{6,15}$
        - name: email
          in: query
          required: false
          description: Customer email. Mutually exclusive with `phone`.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Customer email. Mutually exclusive with `phone`.
            type: string
            format: email
            pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
        - name: limit
          in: query
          required: false
          description: Max orders to return. Default 10.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Max orders to return. Default 10.
            type: integer
            minimum: 1
            maximum: 50
      responses:
        "200":
          description: Customer order history
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  orders:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          minLength: 1
                        order_number:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        order_number_text:
                          type: string
                          description: Display order number, e.g. `#1042`
                        email:
                          anyOf:
                            - type: string
                            - type: "null"
                        phone:
                          anyOf:
                            - type: string
                            - type: "null"
                        order_status:
                          type: string
                        payment_status:
                          type: string
                          description: Projected payment status — read-only view that maps unpaid orders with failed payments to `payment_failed` while leaving DB state untouched.
                        fulfillment_status:
                          type: string
                        return_status:
                          anyOf:
                            - type: string
                            - type: "null"
                        subtotal:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        discount:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        shipping:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        total:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        refunded_amount:
                          type: number
                          minimum: 0
                          description: Amount in ILS as a decimal number (e.g. 12.50)
                        customer_name:
                          anyOf:
                            - type: string
                            - type: "null"
                        shipping_city:
                          anyOf:
                            - type: string
                            - type: "null"
                        cancelled_at:
                          anyOf:
                            - type: string
                              description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                            - type: "null"
                        payment_method:
                          anyOf:
                            - type: string
                            - type: "null"
                        tags:
                          type: array
                          items:
                            type: string
                        created_at:
                          type: string
                          description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                        line_items:
                          type: array
                          items:
                            type: object
                            properties:
                              product_id:
                                anyOf:
                                  - type: string
                                    minLength: 1
                                  - type: "null"
                              variant_id:
                                anyOf:
                                  - type: string
                                    minLength: 1
                                  - type: "null"
                              product_title:
                                type: string
                              variant_title:
                                anyOf:
                                  - type: string
                                  - type: "null"
                              sku:
                                anyOf:
                                  - type: string
                                  - type: "null"
                              quantity:
                                type: integer
                                minimum: 0
                                maximum: 9007199254740991
                              price:
                                type: number
                                minimum: 0
                                description: Amount in ILS as a decimal number (e.g. 12.50)
                              total:
                                type: number
                                minimum: 0
                                description: Amount in ILS as a decimal number (e.g. 12.50)
                              tender:
                                type: string
                                enum:
                                  - ils
                                  - credits
                                description: Per-line tender mode
                              credit_amount_paid_cents:
                                anyOf:
                                  - type: integer
                                    minimum: 0
                                    maximum: 9007199254740991
                                  - type: "null"
                                description: ℳ-credit amount debited for this line, when credits-mode
                            required:
                              - product_id
                              - variant_id
                              - product_title
                              - variant_title
                              - sku
                              - quantity
                              - price
                              - total
                              - tender
                              - credit_amount_paid_cents
                            additionalProperties: {}
                        shipments:
                          type: array
                          items:
                            type: object
                            properties:
                              id:
                                type: string
                                minLength: 1
                              carrier:
                                anyOf:
                                  - type: string
                                  - type: "null"
                              tracking_number:
                                anyOf:
                                  - type: string
                                  - type: "null"
                              status:
                                type: string
                              delivered_at:
                                anyOf:
                                  - type: string
                                    description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                                  - type: "null"
                            required:
                              - id
                              - carrier
                              - tracking_number
                              - status
                              - delivered_at
                            additionalProperties: {}
                      required:
                        - id
                        - order_number
                        - order_number_text
                        - email
                        - phone
                        - order_status
                        - payment_status
                        - fulfillment_status
                        - return_status
                        - subtotal
                        - discount
                        - shipping
                        - total
                        - refunded_amount
                        - customer_name
                        - shipping_city
                        - cancelled_at
                        - payment_method
                        - tags
                        - created_at
                        - line_items
                        - shipments
                      additionalProperties: false
                required:
                  - orders
                additionalProperties: false
        "400":
          description: Missing identifier or invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing or invalid bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Token scope does not permit this endpoint
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/gift-cards:
    get:
      operationId: listGiftCards
      summary: List gift cards owned by a customer (as recipient)
      description: Returns recipient-side gift cards for the phone-keyed customer. Card codes are deliberately omitted — clients should redeem via `POST /gift-cards/redeem` using the code the customer pasted, not by reading the code out of this response.
      tags:
        - Gift Cards
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: phone
          in: query
          required: true
          description: Customer phone, E.164. Required.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Customer phone, E.164. Required.
      responses:
        "200":
          description: Cards belonging to the customer
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  cards:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          minLength: 1
                        amount:
                          type: number
                          minimum: 0
                          description: Original face value in ILS
                        balance:
                          type: number
                          minimum: 0
                          description: Remaining balance in ILS
                        currency:
                          type: string
                          enum:
                            - ILS
                          description: ISO 4217 currency code
                        status:
                          type: string
                          enum:
                            - active
                            - depleted
                        issued_at:
                          type: string
                          description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                        sender_name:
                          anyOf:
                            - type: string
                            - type: "null"
                        greeting_url:
                          anyOf:
                            - type: string
                              format: uri
                            - type: "null"
                          description: Customer-facing greeting page URL on the production domain
                        is_partner_issued:
                          type: boolean
                        order_id:
                          anyOf:
                            - type: string
                              minLength: 1
                            - type: "null"
                      required:
                        - id
                        - amount
                        - balance
                        - currency
                        - status
                        - issued_at
                        - sender_name
                        - greeting_url
                        - is_partner_issued
                        - order_id
                      additionalProperties: false
                required:
                  - cards
                additionalProperties: false
        "400":
          description: Invalid phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/gift-cards/validate:
    get:
      operationId: validateGiftCard
      summary: Public gift-card balance / validity check
      description: Public, unauthenticated endpoint. Rate-limited per source IP. Returns balance + status for a given code. Use this from checkout flows to verify a card before redeeming.
      tags:
        - Gift Cards
      security: []
      parameters:
        - name: code
          in: query
          required: true
          description: Gift card code. Dashes are accepted (case-insensitive); normalized server-side.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^GML[A-Z0-9-]{12,16}$
            description: Gift card code. Dashes are accepted (case-insensitive); normalized server-side.
      responses:
        "200":
          description: Gift card snapshot
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  status:
                    type: string
                    description: "`active` / `depleted` / similar"
                  balance_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                  initial_amount_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                  currency:
                    type: string
                    enum:
                      - ILS
                    description: ISO 4217 currency code
                required:
                  - status
                  - balance_cents
                  - initial_amount_cents
                  - currency
                additionalProperties: false
        "400":
          description: Missing or malformed code
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Card not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "429":
          description: Rate limit exceeded (60/min per IP)
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
      x-rate-limit:
        max: 60
        windowSeconds: 60
        keyedOn: source-ip
  /api/v1/gift-cards/redeem:
    post:
      operationId: redeemGiftCard
      summary: Apply a gift card to an order
      description: Deducts `amount_cents` from the gift card and binds the redemption to the order. Idempotent against the (code, order_id, amount_cents) tuple at the service layer.
      tags:
        - Gift Cards
      security:
        - bearerAuth:
            - full
            - giftcards
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                code:
                  type: string
                  pattern: ^GML[A-Z0-9-]{12,16}$
                  description: Gift card code in `GML-XXXX-XXXX-XXXX` or `GMLXXXXXXXXXXXX` form
                amount_cents:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                  description: Amount in agorot to deduct from the card
                order_id:
                  type: string
                  minLength: 1
                  description: Order the redemption is being applied to
              required:
                - code
                - amount_cents
                - order_id
              additionalProperties: false
      responses:
        "200":
          description: Redemption recorded
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  success:
                    type: boolean
                    const: true
                  redeemed_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                  remaining_balance_cents:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                    description: Amount in integer agorot (100 = ₪1)
                required:
                  - success
                  - redeemed_cents
                  - remaining_balance_cents
                additionalProperties: false
        "400":
          description: Validation error or redemption refused
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Read-only token or scope mismatch
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/payment-links:
    get:
      operationId: listPaymentLinks
      summary: Outstanding payment links for a customer
      description: Returns pending `payment_requests` for unpaid / partially-paid / partially-refunded orders belonging to the phone-keyed customer. URLs use the production domain so the result is safe to forward via WhatsApp without rewrite.
      tags:
        - Payment Links
      security:
        - bearerAuth:
            - full
          phoneIdentifier: []
      parameters:
        - name: phone
          in: query
          required: true
          description: Customer phone, E.164 format. Required.
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Customer phone, E.164 format. Required.
      responses:
        "200":
          description: List of outstanding payment requests
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  payment_links:
                    type: array
                    items:
                      type: object
                      properties:
                        token:
                          type: string
                          description: Opaque token for the public payment page
                        url:
                          type: string
                          format: uri
                          description: Full customer-facing URL on the production domain
                        amount:
                          type: number
                          minimum: 0
                          description: Outstanding amount in ILS
                        currency:
                          type: string
                          enum:
                            - ILS
                          description: ISO 4217 currency code
                        method:
                          anyOf:
                            - type: string
                            - type: "null"
                          description: Payment method hint, e.g. `card` / `bit` / `bank_transfer`
                        order_id:
                          type: string
                          minLength: 1
                        order_number:
                          type: string
                        created_at:
                          type: string
                          description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                        updated_at:
                          type: string
                          description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      required:
                        - token
                        - url
                        - amount
                        - currency
                        - method
                        - order_id
                        - order_number
                        - created_at
                        - updated_at
                      additionalProperties: false
                required:
                  - payment_links
                additionalProperties: false
        "400":
          description: Invalid or missing phone
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Customer not found
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/register:
    post:
      operationId: registerCustomer
      summary: Trusted customer registration intake (WA-inbound + partner)
      description: Designed to be invoked by `wa.makeup.land` on every inbound WhatsApp contact (WA delivery proves phone ownership) or by partner systems with a `register`-scoped token. Orchestrates customer create/update, tag merge, webhook dispatch, WhatsApp template, and on first insert materialises M-Club static opportunities. WA template + webhooks fire only when `created === true`. Returns 201 on create, 200 on update.
      tags:
        - Register
      security:
        - bearerAuth:
            - full
            - register
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                phone:
                  type: string
                  pattern: ^\+\d{6,15}$
                  description: Phone number in E.164 format, e.g. +972501234567
                name:
                  type: string
                email:
                  type: string
                  format: email
                  pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
                tag:
                  description: Convenience single-tag shortcut. Merged with `tags` if both supplied.
                  type: string
                tags:
                  type: array
                  items:
                    type: string
                team:
                  description: "`yes` marks the customer as part of the team. Off by default."
                  type: string
                  enum:
                    - yes
                profile_pic_url:
                  description: Avatar source URL. Must be HTTPS and resolve to an allowlisted host (`*.whatsapp.net`, `*.fbcdn.net`). SSRF-guarded.
                  type: string
                  format: uri
                referred_by_code:
                  description: Referral code — written once on insert, never updated.
                  type: string
                referral_source:
                  type: string
              required:
                - phone
              additionalProperties: false
      responses:
        "200":
          description: Existing customer updated
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  status:
                    type: string
                    const: ok
                  customer:
                    type: object
                    properties: {}
                    additionalProperties: {}
                  created:
                    type: boolean
                  webhook:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - sent
                          - skipped
                          - failed
                          - disabled
                      status_code:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      attempts:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      error:
                        type: string
                    required:
                      - status
                    additionalProperties: false
                  whatsapp:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - sent
                          - skipped
                          - failed
                          - disabled
                      template:
                        type: string
                      error:
                        type: string
                    required:
                      - status
                    additionalProperties: false
                  tags_added:
                    type: array
                    items:
                      type: string
                  tags_already_present:
                    type: array
                    items:
                      type: string
                required:
                  - status
                  - customer
                  - created
                  - webhook
                  - whatsapp
                additionalProperties: false
        "201":
          description: New customer created
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  status:
                    type: string
                    const: ok
                  customer:
                    type: object
                    properties: {}
                    additionalProperties: {}
                  created:
                    type: boolean
                  webhook:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - sent
                          - skipped
                          - failed
                          - disabled
                      status_code:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      attempts:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      error:
                        type: string
                    required:
                      - status
                    additionalProperties: false
                  whatsapp:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - sent
                          - skipped
                          - failed
                          - disabled
                      template:
                        type: string
                      error:
                        type: string
                    required:
                      - status
                    additionalProperties: false
                  tags_added:
                    type: array
                    items:
                      type: string
                  tags_already_present:
                    type: array
                    items:
                      type: string
                required:
                  - status
                  - customer
                  - created
                  - webhook
                  - whatsapp
                additionalProperties: false
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Read-only token, scope mismatch, or no `registration_source` configured for the token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "409":
          description: Phone conflict
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
      x-rate-limit:
        max: 60
        windowSeconds: 60
        keyedOn: token-id
  /api/v1/registrations:
    get:
      operationId: listRegistrations
      summary: Paginated registration audit log
      description: Register-scoped tokens see only registrations belonging to the token's `registration_source`. Full-scope tokens see everything.
      tags:
        - Register
      security:
        - bearerAuth:
            - full
            - register
      parameters:
        - name: phone
          in: query
          required: false
          description: Phone number in E.164 format, e.g. +972501234567
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
            pattern: ^\+\d{6,15}$
            description: Phone number in E.164 format, e.g. +972501234567
        - name: status
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
        - name: tag
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: string
        - name: from
          in: query
          required: false
          description: Inclusive lower bound on registered_at
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Inclusive lower bound on registered_at
            type: string
        - name: to
          in: query
          required: false
          description: Exclusive upper bound on registered_at
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: Exclusive upper bound on registered_at
            type: string
        - name: limit
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: integer
            minimum: 1
            maximum: 100
        - name: page
          in: query
          required: false
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: integer
            minimum: 1
            maximum: 9007199254740991
      responses:
        "200":
          description: Registration audit page
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  registrations:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          exclusiveMinimum: 0
                          maximum: 9007199254740991
                        phone:
                          type: string
                        name:
                          anyOf:
                            - type: string
                            - type: "null"
                        email:
                          anyOf:
                            - type: string
                            - type: "null"
                        tags:
                          type: array
                          items:
                            type: string
                        status:
                          type: string
                        created:
                          type: boolean
                        customer_id:
                          type: string
                          minLength: 1
                        webhook_status:
                          anyOf:
                            - type: string
                            - type: "null"
                        whatsapp_status:
                          anyOf:
                            - type: string
                            - type: "null"
                        registered_at:
                          type: string
                          description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      required:
                        - id
                        - phone
                        - name
                        - email
                        - tags
                        - status
                        - created
                        - customer_id
                        - webhook_status
                        - whatsapp_status
                        - registered_at
                      additionalProperties: false
                  total:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                  page:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  has_more:
                    type: boolean
                required:
                  - registrations
                  - total
                  - page
                  - has_more
                additionalProperties: false
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/registrations/{id}:
    get:
      operationId: getRegistration
      summary: Single registration with full webhook + WhatsApp audit
      tags:
        - Register
      security:
        - bearerAuth:
            - full
            - register
      parameters:
        - name: id
          in: path
          required: true
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            type: integer
            exclusiveMinimum: 0
            maximum: 9007199254740991
      responses:
        "200":
          description: Registration row
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  registration:
                    type: object
                    properties:
                      id:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      phone:
                        type: string
                      name:
                        anyOf:
                          - type: string
                          - type: "null"
                      email:
                        anyOf:
                          - type: string
                          - type: "null"
                      tags:
                        type: array
                        items:
                          type: string
                      status:
                        type: string
                      created:
                        type: boolean
                      customer_id:
                        type: string
                        minLength: 1
                      webhook_status:
                        anyOf:
                          - type: string
                          - type: "null"
                      whatsapp_status:
                        anyOf:
                          - type: string
                          - type: "null"
                      registered_at:
                        type: string
                        description: ISO-8601 timestamp, e.g. 2026-05-23T12:34:56.789Z
                      webhook:
                        type: object
                        properties:
                          status:
                            type: string
                          url:
                            anyOf:
                              - type: string
                                format: uri
                              - type: "null"
                          status_code:
                            anyOf:
                              - type: integer
                                minimum: -9007199254740991
                                maximum: 9007199254740991
                              - type: "null"
                          attempts:
                            type: integer
                            minimum: 0
                            maximum: 9007199254740991
                        required:
                          - status
                          - url
                          - status_code
                          - attempts
                        additionalProperties: false
                      whatsapp:
                        type: object
                        properties:
                          status:
                            type: string
                          template:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - status
                          - template
                        additionalProperties: false
                    required:
                      - id
                      - phone
                      - name
                      - email
                      - tags
                      - status
                      - created
                      - customer_id
                      - webhook_status
                      - whatsapp_status
                      - registered_at
                      - webhook
                      - whatsapp
                    additionalProperties: false
                required:
                  - registration
                additionalProperties: false
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "404":
          description: Not found, or scoped token does not own this registration
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
  /api/v1/proposals:
    post:
      operationId: submitProposals
      summary: Batch-submit catalog enrichment proposals
      description: Inserts new proposals after superseding any prior proposals for the same `(entity_id, field_name)` tuple. Snapshots the current ML value for the audit log. Batch is capped at 200 rows; larger payloads return 413.
      tags:
        - Proposals
      security:
        - bearerAuth:
            - full
            - proposals
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
          schema:
            $schema: https://json-schema.org/draft/2020-12/schema
            description: "Optional client-generated idempotency token (recommended: UUIDv4). Retries with the same key within 24h replay the original response."
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              anyOf:
                - type: array
                  items:
                    type: object
                    properties:
                      entity_type:
                        type: string
                        enum:
                          - product
                        description: Only `product` is accepted in Phase 3.
                      entity_id:
                        type: string
                        minLength: 1
                      field_name:
                        type: string
                        description: Field key from the ENRICH_FIELD_CONTRACT — e.g. `title_he`, `description_he`, `tags`, etc.
                      proposed_value: {}
                      source:
                        type: string
                        description: Provenance label — must be in ENRICH_PROPOSAL_SOURCES (e.g. `manual`, `shopify-import`, `gemini-2.5-pro`, `vps-1.5`).
                      source_detail:
                        type: string
                      confidence:
                        type: number
                        minimum: 0
                        maximum: 1
                      vector:
                        description: Optional embedding vector identifier
                        type: string
                      proposed_by:
                        type: string
                        description: Actor that produced the proposal (admin username, agent id, ...)
                      request_id:
                        type: string
                      source_urls:
                        description: HTTP(S) only. Other schemes are rejected at validation.
                        type: array
                        items:
                          type: string
                          format: uri
                      vps_confidence:
                        type: number
                        minimum: 0
                        maximum: 1
                    required:
                      - entity_type
                      - entity_id
                      - field_name
                      - proposed_value
                      - source
                      - proposed_by
                    additionalProperties: false
                - type: object
                  properties:
                    proposals:
                      type: array
                      items:
                        type: object
                        properties:
                          entity_type:
                            type: string
                            enum:
                              - product
                            description: Only `product` is accepted in Phase 3.
                          entity_id:
                            type: string
                            minLength: 1
                          field_name:
                            type: string
                            description: Field key from the ENRICH_FIELD_CONTRACT — e.g. `title_he`, `description_he`, `tags`, etc.
                          proposed_value: {}
                          source:
                            type: string
                            description: Provenance label — must be in ENRICH_PROPOSAL_SOURCES (e.g. `manual`, `shopify-import`, `gemini-2.5-pro`, `vps-1.5`).
                          source_detail:
                            type: string
                          confidence:
                            type: number
                            minimum: 0
                            maximum: 1
                          vector:
                            description: Optional embedding vector identifier
                            type: string
                          proposed_by:
                            type: string
                            description: Actor that produced the proposal (admin username, agent id, ...)
                          request_id:
                            type: string
                          source_urls:
                            description: HTTP(S) only. Other schemes are rejected at validation.
                            type: array
                            items:
                              type: string
                              format: uri
                          vps_confidence:
                            type: number
                            minimum: 0
                            maximum: 1
                        required:
                          - entity_type
                          - entity_id
                          - field_name
                          - proposed_value
                          - source
                          - proposed_by
                        additionalProperties: false
                    discovered_image_urls:
                      type: array
                      items:
                        type: string
                        format: uri
                  required:
                    - proposals
                  additionalProperties: false
              description: Either a bare array of proposals or `{ proposals, discovered_image_urls }`. When the envelope form is used, `discovered_image_urls` is denormalised onto every proposal.
      responses:
        "200":
          description: Counts of inserted + superseded rows
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  accepted:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                  superseded:
                    type: integer
                    minimum: 0
                    maximum: 9007199254740991
                required:
                  - accepted
                  - superseded
                additionalProperties: false
        "400":
          description: Invalid JSON or envelope
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "401":
          description: Missing bearer token
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "403":
          description: Read-only token or scope mismatch
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "413":
          description: Batch exceeds 200 rows
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
        "422":
          description: Per-row validation errors
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    const: Validation failed
                  row_errors:
                    type: array
                    items:
                      type: object
                      properties:
                        index:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                        field_name:
                          type: string
                        reason:
                          type: string
                      required:
                        - index
                        - field_name
                        - reason
                      additionalProperties: false
                required:
                  - error
                  - row_errors
                additionalProperties: false
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $schema: https://json-schema.org/draft/2020-12/schema
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message (en or he, copy may shift)
                  error_code:
                    description: Stable machine-readable code. Optional today (legacy callers depend on the `error` string field); will become required in the next API version.
                    type: string
                    enum:
                      - invalid_json
                      - invalid_quantity
                      - invalid_phone
                      - invalid_parameter
                      - product_unavailable
                      - variant_mismatch
                      - tender_unavailable
                      - customer_not_found
                      - line_item_not_found
                      - registration_not_found
                      - insufficient_stock
                      - insufficient_credits
                      - line_collision
                      - phone_conflict
                      - validation_failed
                      - scope_mismatch
                      - read_only_token
                      - rate_limited
                      - unauthorized
                      - internal_error
                      - endpoint_not_found
                required:
                  - error
                additionalProperties: {}
                description: Standard V1 error envelope. Additional fields may be present (e.g. `available_cents`, `requested_cents` on 409 insufficient_credits, `row_errors` on 422 validation failures).
