openapi: 3.2.0 info: title: makeup.land Customers 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/`. 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: Customers description: Customer CRUD + tags + opportunities + best deals paths: /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 (case-insensitively; stored lower-case); 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: description: Tags to union in. Tag identity is case-insensitive (`Pro` == `pro`); tags are stored normalized (trimmed, lower-case). 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`. Tags are matched case-insensitively (`Pro` == `pro`) and stored normalized (trimmed, lower-case); the final set is deduplicated. 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 components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: ml_ 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. 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.