# makeup.land — full agent guide > Professional makeup and cosmetics e-commerce for Israel. Hebrew-RTL > storefront with bilingual product data, semantic shade search, virtual > lip + cheek try-on, and a dual-tender pricing model (ILS shekels + > ℳ-credit wallet earned via M Club rewards). This document is the long-form companion to [`https://makeup.land/llms.txt`](https://makeup.land/llms.txt). Use it when you need to understand how to call the API, what data shapes to expect, or how the rewards / payment models work. The canonical machine-readable description is the OpenAPI 3.1 spec at [https://makeup.land/openapi.json](https://makeup.land/openapi.json). ## At a glance - Domain: `makeup.land` (production), Hebrew-primary (`he-IL`). - Catalog: ~1,550 products across 34+ brands. RSS / OpenAI ACP / Google Merchant feeds at `https://makeup.land/api/merchant/*`. - Money: ILS agorot when fields end in `_cents` (₪1 = 100 agorot); ℳ-credits also in agorot via the `credit_price` field on variants. - Languages: Hebrew + English (where enriched). Number / date fields always use Western digits + ISO formats. ## API surface (V1) Base URL: `https://makeup.land/api/v1` ### Discovery surfaces - OpenAPI 3.1 spec — [https://makeup.land/openapi.json](https://makeup.land/openapi.json) / [https://makeup.land/openapi.yaml](https://makeup.land/openapi.yaml) - llms.txt index — [https://makeup.land/llms.txt](https://makeup.land/llms.txt) - AI manifest — [https://makeup.land/.well-known/ai-manifest.json](https://makeup.land/.well-known/ai-manifest.json) - Auth skill manifest — [https://makeup.land/auth.md](https://makeup.land/auth.md) (WorkOS auth.md format) - OAuth Protected Resource Metadata — [https://makeup.land/.well-known/oauth-protected-resource](https://makeup.land/.well-known/oauth-protected-resource) (RFC 9728) - OAuth Authorization Server Metadata — [https://makeup.land/.well-known/oauth-authorization-server](https://makeup.land/.well-known/oauth-authorization-server) (RFC 8414) - Sitemap — [https://makeup.land/sitemap.xml](https://makeup.land/sitemap.xml) - robots.txt — [https://makeup.land/robots.txt](https://makeup.land/robots.txt) ### Endpoint inventory | Method | Path | Auth | Operation ID | |--------|-------------------------------------------------|------------|-----------------------------| | GET | /api/v1/products | Bearer* | listProducts | | GET | /api/v1/brands | Bearer | listBrands | | GET | /api/v1/customers | Bearer | getCustomer | | POST | /api/v1/customers | Bearer | upsertCustomer | | PATCH | /api/v1/customers/{id}/tags | Bearer | patchCustomerTags | | GET | /api/v1/customers/{phone}/opportunities | Bearer | listCustomerOpportunities | | GET | /api/v1/customers/best-deals | Phone-key | getCustomerBestDeals | | GET | /api/v1/cart | Bearer +ph | getCart | | DELETE | /api/v1/cart | Bearer +ph | clearCart | | POST | /api/v1/cart/items | Bearer +ph | addCartItem | | PATCH | /api/v1/cart/items/{lineItemId} | Bearer +ph | patchCartItem | | DELETE | /api/v1/cart/items/{lineItemId} | Bearer +ph | deleteCartItem | | GET | /api/v1/orders | Bearer | listOrders | | GET | /api/v1/gift-cards | Bearer +ph | listGiftCards | | GET | /api/v1/gift-cards/validate | Public | validateGiftCard | | POST | /api/v1/gift-cards/redeem | Bearer | redeemGiftCard | | GET | /api/v1/payment-links | Bearer +ph | listPaymentLinks | | POST | /api/v1/register | Bearer | registerCustomer | | GET | /api/v1/registrations | Bearer | listRegistrations | | GET | /api/v1/registrations/{id} | Bearer | getRegistration | | POST | /api/v1/proposals | Bearer | submitProposals | *`listProducts` is tier-gated: tag-only or no-arg listing is bearer-optional; `q` / `phone` / `include=inventory` / `relevant_to_phone` require a full-scope bearer token. ## Authentication ### Bearer tokens Header: `Authorization: Bearer ml_` Tokens are issued from the admin panel against the `api_tokens` table and carry two access-control axes: - **Scope** — `full` (default), `register`, `giftcards`, or `proposals`. Endpoints accept one or more scopes; mismatched scope returns 403 with `{error: "scope_mismatch"}`. - **read_only flag** — when true, every write (POST/PATCH/DELETE) returns 403 with `{error: "read_only_token"}` regardless of scope. ### Phone-keyed endpoints (Bearer + phone) The endpoints marked **Bearer +ph** in the inventory above — cart, gift-cards list, payment links, best deals, opportunities — require BOTH a bearer token AND a customer phone in E.164 format. The bearer authenticates the caller (your partner integration); the phone selects which customer's resources to return. A bearer without phone is rejected with `400 phone parameter required`; a phone without bearer is rejected with `401 Unauthorized`. - Phone goes in the `?phone=` query parameter for GET/DELETE, or inside the request body for cart-mutating endpoints (POST/PATCH). - Token scope must accept `null` (the default `full` scope qualifies). - Rate-limited per source IP on top of bearer validation. ### Public endpoint `GET /api/v1/gift-cards/validate?code=GML…` is unauthenticated. Format- guarded + rate-limited per IP. Used by checkout flows to verify a gift card before redeeming. ### Anonymous catalog browse `GET /api/v1/products` works without a bearer for **tag**, **brand**, **near_hex**, **hue_family**, and **sort** filters. Bearer becomes required as soon as you pass `phone=`, `include=inventory`, or `relevant_to_phone=` — those return PII-keyed projections. ## Quickstart ### List products with a tag (no auth) ```bash curl -s "https://makeup.land/api/v1/products?tag=lipstick&limit=5" | jq '.products[].title' ``` ### Search by shade (auth required) ```bash curl -s "https://makeup.land/api/v1/products?near_hex=%23C2185B&limit=5" \ -H "Authorization: Bearer ml_..." \ | jq '.products[] | {title, swatches: [.swatches[].hex]}' ``` ### Read a customer's open M-Club opportunities ```bash PHONE_ENCODED=$(printf '%s' "+972501234567" | jq -sRr @uri) curl -s "https://makeup.land/api/v1/customers/$PHONE_ENCODED/opportunities" \ -H "Authorization: Bearer ml_..." ``` ### Add a cart line item (bearer + phone in body) ```bash curl -s -X POST "https://makeup.land/api/v1/cart/items" \ -H "Authorization: Bearer ml_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "phone": "+972501234567", "product_id": "prod_abc", "variant_id": "var_xyz", "quantity": 1, "tender": "ils" }' ``` ### JavaScript / TypeScript ```js const res = await fetch("https://makeup.land/api/v1/products?q=red+lipstick&limit=10", { headers: { Authorization: `Bearer ${process.env.ML_API_TOKEN}` }, }); const { products, has_more } = await res.json(); ``` ## Idempotency All mutation endpoints (POST/PATCH/DELETE) accept an optional `Idempotency-Key` header. Replays with the same key within 24 hours yield the original response — never duplicate effects. Recommended pattern: one UUIDv4 per logical operation. ## Dual-tender pricing (ILS + ℳ-credits) Every product variant has at least one priceable tender: - `price` — ILS shekels, decimal (e.g. `12.50`). - `credit_price` — integer agorot in the ℳ-credit wallet (NULL when ILS-only). Variants where `credit_price IS NOT NULL` can be purchased entirely or partially in credits; cart lines carry a per-line `tender: "ils" | "credits"` field. Credits-mode lines do **not** accrue further rewards (you only earn on the ILS leg). ## M Club rewards Tag-based earn rules match customer tags → product tags (first-match- wins, `*` wildcard). Rewards are credited to a wallet keyed on the customer's phone in agorot. - Earn fires on `payment.completed`. - Refunds claw back proportionally (`refundAmount / order.total`). - Cancellations claw back inline. - Welcome bonus (10ℳ) fires only from phone-verified paths (`/api/v1/register` post-WA-delivery, `/api/auth/login` OTP, admin manual create). Public `/api/checkout` and bulk `POST /api/v1/customers` do **not** mint. ## Error envelope All error responses follow: ```json { "error": "human-readable message", "error_code": "insufficient_stock", "available": 3, "requested": 5 } ``` `error_code` is the stable contract — branch on it. The `error` string copy may shift between releases. Common codes: - `invalid_json`, `invalid_quantity`, `invalid_phone`, `invalid_parameter` — 400 client error. - `product_unavailable`, `variant_mismatch`, `tender_unavailable` — 400 catalog state issue. - `unauthorized`, `scope_mismatch`, `read_only_token` — 401/403 auth. - `customer_not_found`, `line_item_not_found`, `registration_not_found` — 404 not found. - `insufficient_stock`, `insufficient_credits`, `line_collision`, `phone_conflict` — 409 conflict. - `validation_failed` — 422 with `row_errors` array. - `rate_limited` — 429 with `Retry-After` header. - `internal_error` — 500. ## Versioning & deprecation The current major version is `v1`. Breaking changes ship under a new major path (`/api/v2/`); additive changes (new optional fields, new endpoints, new `error_code` values) land in-place on `v1` without notice. When something is scheduled for removal, responses include `Deprecation: true` and `Sunset: ` headers per RFC 8594 / RFC 9745, with at least 6 months between announcement and sunset. No V1 endpoint is currently deprecated. ## Catalog data feeds Read-only feeds suitable for re-indexing without API tokens: - [`/api/merchant/feed`](https://makeup.land/api/merchant/feed) — RSS 2.0 (Google Merchant Center schema). - [`/api/merchant/reviews`](https://makeup.land/api/merchant/reviews) — Google Product Reviews v2.4. - [`/api/merchant/openai/products`](https://makeup.land/api/merchant/openai/products) — OpenAI Agentic Commerce Protocol JSON Lines (ACP feed; credits-only variants are intentionally dropped here since ChatGPT checkout pays in fiat). ## Search semantics `GET /api/v1/products` exposes a hybrid search pipeline: - **`q=...`** — free-text query. Jina v4 multimodal embeddings + Gemini intent parse, joined with a lexical ILIKE fallback for SKU-shaped inputs. Reranked. Hebrew + English supported. - **`near_hex=#RRGGBB`** — ΔE-ranked shade match. Single hex or CSV up to 8 colors (multi-hex triggers a coverage-mode ranking — `coverage=all` finds palettes containing every requested color, `coverage=any` unions). Single-hex default `delta_e_max=80`; multi-hex default 200. - **`hue_family=...`** — post-filter to the named hue bucket(s): `white black gray red coral orange brown nude beige yellow green blue purple pink`. - **`sort=...`** — `price_asc` / `price_desc` / `popularity` / `rating` (Bayesian-shrunk) / `relevance` (default). - **`phone=...`** — when supplied, the response embeds the customer's reward projection per product. Requires full-scope bearer. ## Contact - WhatsApp: +972-77-2233344 - Email: info@makeup.land - Press / partnership: write to the email above with subject "Partnership". This document is maintained alongside the OpenAPI spec; both regenerate on a 1-hour cache TTL. If something here disagrees with [/openapi.json](https://makeup.land/openapi.json), the OpenAPI spec is canonical.