> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tybritelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create order

> Create a new order with HMAC signature verification and idempotency protection.

**🔐 HMAC Signing (REQUIRED)**
  
All order creation requests MUST include HMAC-SHA256 signature for security:
  
1. **Generate Timestamp**: Get current Unix timestamp in seconds
2. **Create Payload**: Concatenate `timestamp + "." + JSON_body`
3. **Sign Payload**: `HMAC-SHA256(hmac_secret, payload)` → base64 encode
4. **Include Headers**:
   - `X-Timestamp`: Unix timestamp (must be within 5 minutes)
   - `X-Signature`: Base64-encoded HMAC signature
  
**Example (Node.js):**
  
**🔄 Idempotency Protection**
  
Include `Idempotency-Key` header to prevent duplicate orders on retry. 
If the same key is used, the original order is returned (not counted against rate limit).
  
**⚠️ Security Notes:**
- HMAC secret is displayed in the Integrations page (Developer section)
- Never expose HMAC secret in client-side code
- Regenerate secret immediately if compromised
- Requests with invalid/missing signatures return 401 Unauthorized
- Timestamps older than 5 minutes are rejected to prevent replay attacks

**🛡️ Server-side price validation (anti-tampering)**

HMAC proves the body wasn't altered in transit; it does **not** prove the amounts are honest.
So the server independently recomputes the order from authoritative data and rejects any
mismatch — you cannot set your own prices or discounts:

- **Item prices** are recomputed from the live catalog. `unit_price` / `total_price` that don't
  match the catalog price → **`400 price_mismatch`**. (The stored order always uses the catalog
  price, never the value you send.)
- **`subtotal`, `tax_amount`, `total_amount`** must reconcile to `subtotal + tax + shipping −
  discount` over those catalog prices → otherwise **`400 price_mismatch`**.
- **`discount_amount`** is validated against the discount the promotions / gift card you claim
  actually grant (computed server-side from `promotion_usages` + `gift_card_redemption`). A
  `discount_amount` greater than that legitimate maximum → **`400 discount_invalid`**. A discount
  with no real promotion/gift card behind it → max is `0`. (You may apply *less* than the maximum.)
- **`shipping_amount`** may not be negative → **`400 price_mismatch`**.

In short: send the **real catalog prices** and the **actual promotions/gift card** the shopper is
entitled to. Don't compute or invent prices/discounts client-side — the server is the authority.




## OpenAPI

````yaml /openapi.yaml post /v1/orders
openapi: 3.1.0
info:
  title: Galactic Core API
  version: 1.0.0
  description: >
    # Galactic Core — The Programmable Commerce Platform
      
    **By Tybrite Labs**
      
    Galactic Core is the programmable interface layer that transforms GalacticOS
    into a globally 

    accessible Backend-as-a-Service (BaaS) for commerce. Rather than rebuilding
    commerce logic 

    from scratch, Galactic Core re-exposes the operational depth of a
    production-grade retail 

    system through clean, versioned REST APIs and real-time webhooks.
      
    ## Why Galactic Core?
      
    **Commerce Infrastructure, Not Commerce Software**
      
    Galactic Core is not an application or a dashboard—it is **infrastructure**.
    The programmable 

    foundation upon which commerce applications are built. Developers inherit
    years of operational 

    hardening through simple API calls:
      
    - **Inventory management** with stock tracking and reorder logic

    - **Order processing** with payment reconciliation and fulfillment workflows

    - **Customer analytics** with RFM segmentation and purchase history

    - **Double-entry accounting** with automatic journal entries

    - **Multi-provider payments** (Stripe, PayPal, Paystack, M-Pesa)

    - **AI-powered recommendations** using semantic embeddings

    - **Dynamic pricing** with hierarchical discount rules
      
    ## Ship in Days, Not Months
      
    Stop rebuilding the same commerce infrastructure for every project. Galactic
    Core provides 

    production-ready primitives that eliminate months of development work:
      
    ✅ **No data silos** — All channels share the same operational reality  

    ✅ **No duplicated logic** — Business rules are centralized and consistent  

    ✅ **No fragile integrations** — Everything is part of one coherent system  

    ✅ **Complete auditability** — Every state change is tracked and traceable  
      
    ## Platform Capabilities
      
    **Core Commerce**

    - Products, Collections, Categories, Specifications

    - Orders with automatic inventory sync and accounting

    - Customers with RFM analytics and store metrics

    - Multi-provider payment processing with webhook automation
      
    **Advanced Features**

    - AI-powered semantic search and recommendations

    - Dynamic pricing engine with hierarchical rules

    - Cart & wishlist persistence with anonymous session support

    - Customer authentication with automatic record linking

    - Gift cards with full accounting integration

    - Promotions with usage tracking and discount attribution

    - Commerce-aware CMS with shoppable content
      
    **Global by Design**

    - Multi-currency support (135+ currencies via Stripe)

    - Regional payment methods (mobile money for Africa)

    - Multi-store architecture with complete data isolation

    - Edge-optimized delivery (10-50ms response times)
      
    ## Authentication
      
    All API requests require authentication using an API key in the
    `Authorization` header:
      
    ```

    Authorization: Bearer tybrite_sk_live_YOUR_API_KEY

    ```
      
    ### API Key Types
      
    **Secret Keys** (Server-Side Only)

    - Production: `tybrite_sk_live_*`

    - Sandbox: `tybrite_sk_test_*`

    - Full read/write access

    - ⚠️ Never expose in client-side code

    - **Required for:**
      - All write operations (POST, PUT, PATCH, DELETE)
      - Authentication endpoints (register, login, logout, etc.)
      - Payment verification (`POST /v1/payments/verify`)
      - AI recommendations (`POST /v1/recommendations`)
      - Order management
      - Customer management
      
    **Publishable Keys** (Client-Safe)

    - Production: `tybrite_pk_live_*`

    - Sandbox: `tybrite_pk_test_*`

    - Read-only access (GET requests only)

    - ✅ Safe for client-side use

    - **Allowed for:**
      - Product browsing
      - Search (both GET and POST semantic search)
      - Category/taxonomy browsing
      - CMS content retrieval
      - Pricing queries
      - Shipping calculations
      - Cart viewing (read-only)
      
    ### Key Type Enforcement
      
    Attempting to use a publishable key for write operations or restricted
    endpoints will return:

    ```json

    {
      "error": {
        "code": "forbidden",
        "message": "This operation requires a secret key. Publishable keys have read-only access.",
        "hint": "Use a secret key (tybrite_sk_*) for write operations"
      }
    }

    ```

    HTTP Status: 403 Forbidden
      
    ### Obtaining API Keys
      
    Generate API keys from your Tybrite dashboard under the Integrations page
    (Developer section).
      
    ## Environments


    The API uses a single endpoint (`https://api.tybritelabs.com`) with the
    environment determined by your API key prefix:

    - Use `tybrite_*_live_*` keys for production data

    - Use `tybrite_*_test_*` keys for sandbox/testing data


    Sandbox and production data are fully isolated — orders, customers, carts,
    and wishlists created with a test key are only visible to other test keys.
    Payment providers are automatically switched to test mode when using a
    sandbox key, regardless of your store's payment settings.


    Every authenticated response includes a `Tybrite-Environment` header
    confirming which environment resolved.


    ## Technical Features
      
    - **Ultra-Low Latency**: 10-50ms response times via edge caching

    - **Multi-tier caching** for low-latency reads.

    - **Idempotency Protection**: Prevent duplicate orders on retry

    - **Field Filtering**: Request only the fields you need (50-90% bandwidth
    reduction)

    - **Automatic Accounting**: Double-entry journal entries for all
    transactions

    - **Webhook Automation**: Real-time payment status updates and order
    processing


    ## Rate Limiting & Usage Quota


    Two independent controls protect the API. They return **different `429`
    codes** — handle them

    differently:


    | `429` code | What it means | `Retry-After` behavior | Counts toward your
    monthly quota? |

    | --- | --- | --- | --- |

    | `rate_limited` | An **abuse throttle** tripped (too many requests, too
    fast, from one client or key). Responses carry an `X-RateLimit-Scope: abuse`
    header. | Transient — slow down and retry shortly | **No** — throttled
    requests never consume your plan's monthly allowance |

    | `quota_exceeded` | You've reached your plan's **monthly request
    allowance**. | Resolves on the next monthly cycle, or sooner if you upgrade
    | n/a (this *is* the monthly meter) |


    ### Abuse rate limits (`rate_limited`)


    These are flat per-client throttles (not tied to your plan tier):


    **Publishable keys:**

    - 1,000 requests/hour per IP address

    - 10,000 requests/hour and 50,000 requests/day per API key


    **Secret keys:**

    - 10,000 requests/hour and 100,000 requests/day per API key


    ### Monthly usage quota (`quota_exceeded`)


    Your plan includes a monthly request allowance, reset on the 1st of each
    month (UTC):


    - **Starter:** 100,000 requests/month

    - **Growth:** 1,000,000 requests/month

    - **Premium:** 5,000,000 requests/month

    - **Enterprise:** unmetered


    (During a free trial the allowance is one-fifth of the plan.)
    Abuse-throttled requests are **not**

    counted against this allowance, so a traffic spike or scraping attempt can't
    quietly burn through

    your plan.


    ### Endpoint-Specific Limits


    **Payment Operations** (Additional limit on top of the above):

    - `POST /v1/payments/initialize`: 100 requests/hour per API key

    - Applies to both publishable and secret keys

    - Prevents payment spam and fraud attempts

    - Returns `429 rate_limited` if exceeded


    **Order Operations** (Enforced via idempotency):

    - `POST /v1/orders`: Requires `Idempotency-Key` header

    - `PATCH /v1/orders/:id`: Requires `Idempotency-Key` header

    - Duplicate keys return existing order/state (not counted against rate
    limits)

    - Prevents accidental duplicate orders and duplicate update processing


    **Authentication Operations**:

    - All auth endpoints: 10 requests/minute per IP address

    - Prevents brute force attacks

    - Applies to login, register, password reset, etc.


    Rate limit headers are included in responses:

    - `X-RateLimit-Limit`: Maximum requests allowed

    - `X-RateLimit-Remaining`: Requests remaining

    - `X-RateLimit-Reset`: Unix timestamp when the limit resets

    - `X-RateLimit-Scope: abuse`: present only on `rate_limited` responses
    (distinguishes an abuse
      throttle from the monthly quota)

    When an abuse limit is exceeded, the API returns HTTP 429 with error
    details:

    ```json

    {
      "error": {
        "code": "rate_limited",
        "message": "Too many requests for this API key. Please slow down and try again shortly.",
        "remaining": 0,
        "reset": 1739750400
      }
    }

    ```


    ## Special Headers


    ### Idempotency-Key (Required for Order Operations)


    **Endpoints:** `POST /v1/orders`, `PATCH /v1/orders/:id`


    Prevents duplicate order creation and duplicate update processing on network
    retries. If you retry a request with the same 

    idempotency key, the API returns the existing order/state instead of
    re-processing.


    ```

    POST /v1/orders

    Idempotency-Key: order-2026-02-16-abc123
      
    PATCH /v1/orders/922d235e-48c9-4933-abcd-88cb2a264f86

    Idempotency-Key: update-shipping-922d235e-1771524100

    ```


    **Requirements:**

    - Must be unique per operation (use different keys for create vs update)

    - Recommended formats:
      - Order creation: `order-{date}-{unique-id}`
      - Order updates: `update-{operation}-{order_id}-{timestamp}`
    - Stored for 24 hours

    - Returns existing order/state if key matches


    ### x-auth-token (Required for Customer Authentication)


    **Endpoints:**

    - `GET /v1/auth/me` - Get current authenticated customer

    - `POST /v1/auth/update-password` - Update customer password


    Contains the authentication token obtained from login/register endpoints.


    ```

    GET /v1/auth/me

    Authorization: Bearer tybrite_sk_live_YOUR_API_KEY

    x-auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

    ```


    **Flow:**

    1. Customer logs in via `POST /v1/auth/login`

    2. Response includes `access_token`

    3. Use `access_token` as `x-auth-token` header value

    4. Token expires after session timeout (default 1 hour)


    ### X-Session-Id (Optional for Anonymous Carts)


    **Endpoints:** All cart endpoints (`/v1/cart/*`)


    Enables anonymous cart functionality before customer authentication.
    Generate a unique 

    session ID on the client and include it in cart requests.


    ```

    POST /v1/cart/items

    Authorization: Bearer tybrite_pk_live_YOUR_API_KEY

    X-Session-Id: session-abc123-xyz789

    ```


    **Behavior:**

    - If `X-Session-Id` provided: Cart associated with session (anonymous)

    - If `customer_id` provided: Cart associated with customer account

    - If both provided: `customer_id` takes precedence

    - If neither provided: Returns 400 error


    **Session Management:**

    - Generate UUID or random string on client

    - Store in localStorage/sessionStorage

    - Persist across page reloads

    - When customer logs in, migrate cart using `customer_id`


    ### Tybrite-Environment (Response Header)


    Every authenticated response includes a `Tybrite-Environment` header that
    confirms which environment your API key resolved to. Use this to verify that
    test keys are hitting sandbox data and live keys are hitting production
    data.


    ```

    Tybrite-Environment: sandbox

    ```


    Possible values: `production`, `sandbox`.


    ## Caching & Performance


    ### ETag Support


    All GET endpoints support ETag caching for efficient revalidation:


    ```

    # First request

    GET /v1/products/abc-123

    Response: 200 OK, ETag: "a1b2c3d4"


    # Subsequent request

    GET /v1/products/abc-123

    If-None-Match: "a1b2c3d4"

    Response: 304 Not Modified (no body)

    ```


    ### Cache Headers


    - `ETag`: Resource version identifier

    - `Cache-Control`: Caching directives

    - `X-Cache`: Cache status (HIT, MISS, HIT-304)


    ## Field Filtering


    Request only specific fields to reduce bandwidth:


    ```

    GET /v1/products?fields=id,name,price,stock

    ```


    Supports nested fields with dot notation:


    ```

    GET /v1/products?fields=id,name,attributes.color,attributes.size

    ```


    ## Error Handling


    All errors follow a consistent format:


    ```json

    {
      "error": {
        "code": "error_code",
        "message": "Human-readable error message",
        "details": "Additional context (optional)"
      }
    }

    ```


    ### Common Error Codes


    - `unauthorized` (401): Invalid or missing API key

    - `forbidden` (403): API key lacks required permissions

    - `not_found` (404): Resource not found

    - `invalid_request` (400): Malformed request or missing fields

    - `rate_limited` (429): Abuse throttle tripped — too many requests too fast
    (carries `X-RateLimit-Scope: abuse`; not counted against your monthly quota)

    - `quota_exceeded` (429): Monthly plan request allowance reached

    - `server_error` (500): Internal server error

    - `service_unavailable` (503): Service temporarily unavailable


    ## Idempotency


    Order creation supports idempotency to prevent duplicates on retry:


    ```

    POST /v1/orders

    Idempotency-Key: order-2026-02-03-abc123

    ```


    Retrying with the same key returns the existing order instead of creating a
    duplicate.


    ## Pagination


    List endpoints are **cursor-based**. Pass a `limit` (page size), and follow
    the

    `pagination.next_cursor` from each response to fetch the next page — there
    is no

    numeric `offset` or page number.


    ```

    GET /v1/products?limit=50

    GET
    /v1/products?limit=50&cursor=eyJwcm9kdWN0X2lkIjoiZDhjZWEyNzctOWJiNi00OTQyIn0=

    ```


    Each response carries a `pagination` object:


    ```json

    {
      "products": [...],
      "pagination": {
        "limit": 50,
        "has_more": true,
        "next_cursor": "eyJwcm9kdWN0X2lkIjoiZDhjZWEyNzctOWJiNi00OTQyIn0="
      }
    }

    ```


    When `has_more` is `false`, `next_cursor` is `null` and you've reached the
    last page.

    Cursors are opaque — pass them back verbatim; don't construct or parse them.
  contact:
    name: Galactic Core API Support - Tybrite Labs
    email: support@tybritelabs.com
    url: https://tybritelabs.com
  license:
    name: Proprietary
    url: https://tybritelabs.com/terms-of-service
servers:
  - url: https://api.tybritelabs.com
    description: Production API
security:
  - BearerAuth: []
tags:
  - name: System
    description: API information and health checks
  - name: Authentication
    description: >
      Customer authentication and session management.
        
      **⚠️ SECRET KEY REQUIRED**: All authentication endpoints require a secret
      key (tybrite_sk_*).

      Publishable keys will return 403 Forbidden.
        
      These endpoints manage customer accounts, login sessions, password resets,
      and OTP verification.

      Use these to build customer-facing authentication flows in your
      storefront.
        
      **Special Headers:**

      - `x-auth-token`: Required for `GET /v1/auth/me` and `POST
      /v1/auth/update-password`

      - Contains the authentication token from login/register response

      - Token expires after session timeout (default 1 hour)
        
      **Rate Limiting:**

      - All authentication endpoints: 10 requests/minute per IP address

      - Prevents brute force attacks on customer accounts
  - name: Products
    description: Product catalog management
  - name: Orders
    description: >
      Order lifecycle management with automatic inventory sync and accounting.
        
      **Special Headers:**

      - `Idempotency-Key`: **REQUIRED** for `POST /v1/orders` and `PATCH
      /v1/orders/:id`
        - Prevents duplicate order creation and duplicate update processing on network retries
        - Recommended formats:
          - Order creation: `order-{date}-{unique-id}`
          - Order updates: `update-{operation}-{order_id}-{timestamp}`
        - Stored for 24 hours
        - Retrying with same key returns existing order/state (not counted against rate limit)
      - `X-Timestamp`: **REQUIRED** for `POST /v1/orders` and `PATCH
      /v1/orders/:id`
        - Unix timestamp in seconds (current time)
        - Must be within 5 minutes of server time
        - Used to prevent replay attacks
      - `X-Signature`: **REQUIRED** for `POST /v1/orders` and `PATCH
      /v1/orders/:id`
        - HMAC-SHA256 signature of payload (timestamp + "." + request_body), base64-encoded
        - Sign using your HMAC secret from the Integrations page (Developer section)
        - Requests with invalid/missing signatures return 401 Unauthorized
        
      **Automatic Processing:**

      - Inventory automatically decremented when payment_status changes to
      'paid'

      - Double-entry accounting journal entries created automatically

      - Payment status tracked and updated via webhooks

      - Gift card redemptions and promotion usage tracked with idempotency
      protection
  - name: Customers
    description: Customer profile management
  - name: Cart & Wishlist
    description: |
      Shopping cart and wishlist operations with anonymous session support.
        
      **Special Headers:**
      - `X-Session-Id`: Optional header for anonymous cart functionality
      - Generate a unique session ID on the client (UUID or random string)
      - Store in localStorage/sessionStorage for persistence
      - When customer logs in, migrate cart by providing `customer_id` instead
        
      **Cart Association:**
      - If `X-Session-Id` provided: Cart associated with session (anonymous)
      - If `customer_id` provided: Cart associated with customer account
      - If both provided: `customer_id` takes precedence
      - If neither provided: Returns 400 error
        
      **Use Cases:**
      - Anonymous browsing: Use `X-Session-Id` before customer authentication
      - Logged-in customers: Use `customer_id` parameter
      - Cart migration: Switch from session to customer_id after login
  - name: Search
    description: >
      Semantic search with AI embeddings and simple text search.
        
      **Key Type Support:**

      - `GET /v1/search` (simple text search): Both secret and publishable keys
      allowed

      - `POST /v1/search` (semantic search): Both secret and publishable keys
      allowed
        
      Note: Despite using POST method, semantic search is a read-only operation
      and works

      with publishable keys. The POST method is used to support complex request
      bodies.
  - name: Recommendations
    description: >
      AI-powered product recommendations using semantic embeddings and
      collaborative filtering.
        
      **⚠️ SECRET KEY REQUIRED**: Recommendation generation requires a secret
      key (tybrite_sk_*).

      Publishable keys will return 403 Forbidden.
        
      This restriction protects the computational resources and AI model access
      used for

      generating personalized recommendations.
  - name: Discovery
    description: >
      Windowed storefront discovery lists derived from live shopper signals —
      most-viewed,

      most-added-to-cart, and best-converting products over a time window (1h /
      1d / 7d / 30d).

      Use them to power "Popular now" shelves, "Trending in carts" sections, and
      PDP "hot" badges.


      Publishable-key accessible (safe in a browser/app) and available on every
      plan. Distinct from

      Recommendations: these are simple, non-personalized signal rankings, not
      the ML engine.
  - name: Events
    description: >
      Storefront interaction events (product views, add-to-cart,
      add-to-wishlist). These power the

      `next` recommendation type — the products a shopper is most likely to view
      or add next based

      on their current session. Publishable keys are accepted so events can be
      sent from the browser.
  - name: Tax
    description: >
      Tax estimation for checkout. Preview the tax for a shipping destination
      and cart before placing

      an order, so the storefront can show and charge the final tax-inclusive
      total. Publishable keys

      are accepted so the estimate can be fetched from the browser during
      checkout.
  - name: Analytics
    description: >
      First-party storefront analytics capture. Fire a lightweight page-view /
      session-start event

      from the storefront on each route change; device, browser, and approximate
      location are derived

      automatically from the request. These events power the merchant's Store
      Analytics dashboard

      (traffic, audience, conversion funnel, and revenue-by-source). Publishable
      keys are accepted so

      events can be sent directly from the browser.
  - name: Taxonomy
    description: Categories and subcategories
  - name: CMS
    description: Content management (posts, lookbooks)
  - name: Payments
    description: >
      Multi-provider payment processing (Stripe, PayPal, Paystack, M-Pesa).
        
      **Special Headers:**

      - `Idempotency-Key`: **REQUIRED** for `POST /v1/payments/initialize`
        - Unique key to prevent duplicate payment initialization (e.g., payment-{timestamp}-{random})
        - If you retry with the same key, the original payment initialization is returned
        - Prevents duplicate charges on network retries
      - `X-Timestamp`: **REQUIRED** for `POST /v1/payments/initialize`
        - Unix timestamp in seconds (current time)
        - Must be within 5 minutes of server time
        - Used to prevent replay attacks
      - `X-Signature`: **REQUIRED** for `POST /v1/payments/initialize`
        - HMAC-SHA256 signature of payload (timestamp + "." + request_body), base64-encoded
        - Sign using your HMAC secret from the Integrations page (Developer section)
        - Requests with invalid/missing signatures return 401 Unauthorized
        
      **Key Type Requirements:**

      - `GET /v1/payments/methods`: Both secret and publishable keys allowed

      - `POST /v1/payments/initialize`: Secret key required

      - `POST /v1/payments/verify`: **SECRET KEY REQUIRED** (read operation but
      sensitive)
        
      Payment verification requires a secret key because it accesses sensitive
      payment status

      information that should not be exposed to client-side code.
        
      **Rate Limiting:**

      - Payment initialization has an additional rate limit of 100 requests/hour
      per API key

      - This is on top of the standard abuse limits (publishable: 1,000/hour per
      IP + 10,000/hour & 50,000/day per key; secret: 10,000/hour & 100,000/day
      per key)

      - Exceeding any of these returns `429 rate_limited` (carries
      `X-RateLimit-Scope: abuse`; not counted against your monthly quota)

      - Prevents payment spam and fraud attempts
  - name: Shipping
    description: Shipping zones and delivery cost calculation
  - name: Pricing
    description: Dynamic pricing engine
  - name: Promotions
    description: Marketing promotions and discounts
  - name: Gift Cards
    description: Gift card management
  - name: Messaging
    description: Real-time customer support messaging
  - name: GC Connect
    description: >
      Hosted authorization flow for connecting third-party tools to a merchant's
      store.


      **Connect your GC Store** lets developers build integrations where
      merchants

      authorize access with a single click — no API key copying required. The
      result

      is a permanent key pair scoped to the merchant's store and the permissions
      they

      granted.


      **Flow overview:**

      1. Redirect the merchant to the consent page with your `client_id`,
      `redirect_uri`,
         `scope`, and `state`.
      2. The merchant logs in (if not already) and approves the connection.

      3. Galactic Core redirects back to your `redirect_uri` with a short-lived
      `code`.

      4. Your **server** calls `POST /v1/connect/token` to exchange the code for
      `sk` + `pk`.

      5. Store the `sk` securely server-side. Use it for all subsequent API
      calls on behalf
         of the merchant.

      **Keys are permanent** — they remain active until the merchant disconnects
      your

      application from their Settings panel, or you call `POST
      /v1/connect/revoke`.


      **To integrate**, your application must be approved by Galactic Core.
      Apply via the

      [Developer Portal](https://gc.tybritelabs.com/developers).
  - name: Reviews
    description: >
      Product reviews and ratings submitted by customers, moderated by
      merchants.


      **Key type behavior:**

      - Publishable keys (`tybrite_pk_*`) — list and get approved reviews,
      submit reviews
        (requires an additional customer session token), and mark reviews helpful.
      - Secret keys (`tybrite_sk_*`) — all of the above, plus view
      pending/rejected reviews,
        approve or reject submissions, and delete any review.

      **Review lifecycle:**

      1. A logged-in customer submits a review (`POST /v1/reviews`). It starts
      as `pending`.

      2. The merchant reviews pending submissions in their dashboard and calls
         `PATCH /v1/reviews/{id}` to approve or reject.
      3. Once approved, the review appears in public listing results.


      **Verified purchases:** Supply the `order_id` when submitting. If the
      order belongs to

      the authenticated customer, the review is automatically marked
      `verified_purchase: true`.


      **Duplicate prevention:** A customer may only have one non-rejected review
      per product.

      A second submission returns `409 Conflict`.


      **Special headers:**

      - `x-auth-token`: Customer session JWT — required for `POST /v1/reviews`
      (submit)
        and for `DELETE /v1/reviews/{id}` when using a publishable key to delete own review.
        Obtained from `POST /v1/auth/login` or `POST /v1/auth/verify-otp`.
  - name: Returns
    description: >
      Return requests a shopper lodges against their own online orders.


      **Key type behavior:**

      - The reason-code list (`GET /v1/returns/reasons`) needs only an API key —
      no
        customer session — so you can build the reason picker before sign-in.
      - Listing, getting, and creating returns require an API key **and** a
      customer
        session: either `x-auth-token` (a GC-native session token) or
        `x-external-auth` (a bring-your-own-auth assertion).

      A customer can only create, list, and track their OWN returns. Approving,

      processing refunds or store credit, restocking, and rejecting items are

      store-management tasks handled in the merchant admin, not through this
      API.


      **Special headers:**

      - `x-auth-token` **or** `x-external-auth`: identifies the signed-in
      customer on
        `GET /v1/returns`, `GET /v1/returns/{id}`, and `POST /v1/returns`.
  - name: Disputes
    description: >
      Disputes a shopper raises on their own marketplace orders when something
      goes

      wrong — the item never arrived, arrived damaged, or was not as described.


      Disputes are a marketplace capability: the operator sits between the
      shopper and

      the merchant, reviews an open dispute, and applies a resolution (a refund,
      store

      credit, or another remedy). This API is the shopper's side of that flow —
      open a

      dispute, list and track your own, add a message, and cancel one you
      opened.

      Reviewing and resolving disputes are operator and merchant tasks handled
      in the

      admin dashboards, not through this API.


      **Key type behavior:**

      - The reason-code list (`GET /v1/disputes/reasons`) needs only an API key
      — no
        customer session — so you can build the reason picker before sign-in.
      - Listing, getting, opening, messaging, and cancelling disputes require an
      API key
        **and** a customer session: either `x-auth-token` (a GC-native session token) or
        `x-external-auth` (a bring-your-own-auth assertion).

      A shopper can only open, list, track, message, and cancel their OWN
      disputes.


      **Special headers:**

      - `x-auth-token` **or** `x-external-auth`: identifies the signed-in
      customer on the
        per-shopper dispute endpoints.
  - name: Marketplace
    description: >
      Endpoints for building a multi-merchant marketplace storefront.


      These endpoints let a marketplace storefront read the marketplace's
      identity and

      branding (and a single merchant's store information within it), resolve
      commission,

      check out a cart spanning multiple merchants in a single payment, and read
      a

      shopper's unified profile across every merchant they have shopped with.


      To narrow aggregated marketplace catalog results to a single merchant's
      "shop page",

      pass `?store_id=<merchant>` to the products, categories, subcategories,
      and search

      endpoints (operator key only).


      **Key types:**

      - Marketplace information, unified checkout, and the unified customer
      profile use
        the marketplace operator key. Commission resolution accepts the marketplace operator
        key or a merchant secret key.

      Merchant payment onboarding and operator payout runs are merchant/operator
      back-office

      tasks handled in the admin interface, not through this API.
  - name: Ingestion
    description: >
      Sync an external product catalog INTO Galactic Core.


      **⚠️ SECRET KEY + SIGNATURE REQUIRED**: `POST /v1/ingest/products`
      requires a secret

      key (`tybrite_sk_*`) **and** a request signature. Publishable keys return
      403; a missing

      or invalid signature returns 401. This is the one write endpoint where
      both are mandatory.


      Use ingestion when a merchant's own system (an ERP, a PIM, another
      storefront, a

      spreadsheet export) is the source of truth for products and you want to
      push or sync

      that catalog into Galactic Core — rather than entering products by hand.


      **What it does:**

      - Accepts a batch of products as **JSON, XML, or CSV** (set the
      `Content-Type`, or
        `?format=`). One request can carry many products.
      - **Upserts by SKU**: a product whose SKU already exists is updated; a new
      SKU is created.
        Use `?strategy=create_only` to skip existing SKUs instead of updating them.
      - **Groups variants**: rows that share a `product_group` become variants
      of a single
        product (e.g. a shirt in S/M/L). Rows without a group are single-variant products.
      - **Reports per row**: the response carries a `summary` (created / updated
      / skipped /
        failed counts) and an `errors` array naming the row, SKU, field, and reason for every
        rejected row — so a partly-valid batch still imports the good rows and tells you exactly
        what to fix.

      **Signing:** sign each request the same way as orders/payments —
      `X-Timestamp` (unix

      seconds) + `X-Signature` (HMAC-SHA256 of `timestamp + "." + raw_body`,
      base64), within a

      5-minute window. An `Idempotency-Key` is required so a retried batch is
      processed once.


      **Test before you integrate (no key needed):**

      - `GET /v1/ingest/sample?format=json|xml|csv` returns a valid sample feed
      to copy.

      - `POST /v1/ingest/test` validates a feed and returns the same
      `summary`/`errors` you'd
        get from a real import — **without writing anything**. Both are rate-limited to 60/hour
        per IP.

      **Scheduled sync:** merchants can also register a feed URL in their
      dashboard for Galactic

      Core to pull and sync on a schedule; a `feed.sync.completed` webhook fires
      after each run.
  - name: Webhooks
    description: >
      Outbound webhook management for merchant integrators.


      **⚠️ SECRET KEY REQUIRED**: All webhook endpoints require a secret key
      (`tybrite_sk_*`).

      Publishable keys will return 403 Forbidden.


      Webhooks allow your systems to receive real-time notifications when events
      occur in your

      store — eliminating the need to poll the API.


      **Delivery contract:**

      - **At-least-once** delivery — endpoints must be idempotent. Use
      `event.id` for dedup.

      - **HMAC-signed** — every request carries `X-Tybrite-Signature:
      t=<timestamp>,v1=<hmac_sha256>`.
        Verify with the `signing_secret` returned at endpoint creation.
      - **Payload size** — capped at 256 KB per delivery.

      - **Ordering** — best-effort, not guaranteed. Use event timestamps for
      sequencing.


      **Signature verification:**

      ```typescript

      const [tPart, v1Part] = req.headers['x-tybrite-signature'].split(',');

      const timestamp = tPart.replace('t=', '');

      const receivedSig = v1Part.replace('v1=', '');

      const payload = `${timestamp}.${rawBody}`;

      const expected = hmacSha256Hex(signingSecret, payload);

      if (receivedSig !== expected) throw new Error('Invalid signature');

      // Replay check: reject if |now - timestamp| > 300 seconds

      ```


      **Rate Limiting:** 500 requests/hour per secret key.
  - name: Sandbox
    description: >
      Developer tooling for the sandbox (test) environment — make your test runs
      fast and repeatable.


      **⚠️ SECRET TEST KEY REQUIRED**: every Sandbox endpoint requires a
      `tybrite_sk_test_*` key and

      only ever touches your sandbox data; publishable keys and live keys are
      rejected (403). Your

      live data and store configuration are never affected.


      - **Reset** (`POST /v1/sandbox/reset`, or `DELETE /v1/sandbox/data`) —
      wipe all your sandbox
        test data instantly instead of waiting for the automatic 30-day cleanup.
      - **Fast-forward time** (`POST /v1/sandbox/time`) — advance your sandbox
      by N days so abandoned
        carts, stock-reservation windows and analytics roll forward without waiting. (Subscription
        trials/renewals and promotion/gift-card expiry are not time-shifted — see the endpoint.)
      - **Replay a webhook** (`POST /v1/sandbox/webhooks/replay`) — re-send a
      recorded sandbox webhook
        event to your endpoints without recreating the underlying record.
  - name: B2B
    description: >
      GC B2B — the buyer-side wholesale flow: request a quote (RFQ), receive and
      accept

      a quote, track the resulting purchase order, and pay invoices on terms.


      Every endpoint acts on the buyer's own records and requires the buyer's
      session

      (`x-auth-token` or `x-external-auth`) alongside the API key. Supplier-side
      actions —

      pricing quotes, confirming purchase orders, fulfilling, and credit
      decisions — are

      handled in the supplier's admin, never through this API. Available only on

      deployments with B2B enabled.
externalDocs:
  description: Galactic Core Documentation
  url: https://docs.tybritelabs.com/galactic-core
paths:
  /v1/orders:
    post:
      tags:
        - Orders
      summary: Create order
      description: >
        Create a new order with HMAC signature verification and idempotency
        protection.


        **🔐 HMAC Signing (REQUIRED)**
          
        All order creation requests MUST include HMAC-SHA256 signature for
        security:
          
        1. **Generate Timestamp**: Get current Unix timestamp in seconds

        2. **Create Payload**: Concatenate `timestamp + "." + JSON_body`

        3. **Sign Payload**: `HMAC-SHA256(hmac_secret, payload)` → base64 encode

        4. **Include Headers**:
           - `X-Timestamp`: Unix timestamp (must be within 5 minutes)
           - `X-Signature`: Base64-encoded HMAC signature
          
        **Example (Node.js):**
          
        **🔄 Idempotency Protection**
          
        Include `Idempotency-Key` header to prevent duplicate orders on retry. 

        If the same key is used, the original order is returned (not counted
        against rate limit).
          
        **⚠️ Security Notes:**

        - HMAC secret is displayed in the Integrations page (Developer section)

        - Never expose HMAC secret in client-side code

        - Regenerate secret immediately if compromised

        - Requests with invalid/missing signatures return 401 Unauthorized

        - Timestamps older than 5 minutes are rejected to prevent replay attacks


        **🛡️ Server-side price validation (anti-tampering)**


        HMAC proves the body wasn't altered in transit; it does **not** prove
        the amounts are honest.

        So the server independently recomputes the order from authoritative data
        and rejects any

        mismatch — you cannot set your own prices or discounts:


        - **Item prices** are recomputed from the live catalog. `unit_price` /
        `total_price` that don't
          match the catalog price → **`400 price_mismatch`**. (The stored order always uses the catalog
          price, never the value you send.)
        - **`subtotal`, `tax_amount`, `total_amount`** must reconcile to
        `subtotal + tax + shipping −
          discount` over those catalog prices → otherwise **`400 price_mismatch`**.
        - **`discount_amount`** is validated against the discount the promotions
        / gift card you claim
          actually grant (computed server-side from `promotion_usages` + `gift_card_redemption`). A
          `discount_amount` greater than that legitimate maximum → **`400 discount_invalid`**. A discount
          with no real promotion/gift card behind it → max is `0`. (You may apply *less* than the maximum.)
        - **`shipping_amount`** may not be negative → **`400 price_mismatch`**.


        In short: send the **real catalog prices** and the **actual
        promotions/gift card** the shopper is

        entitled to. Don't compute or invent prices/discounts client-side — the
        server is the authority.
      operationId: createOrder
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          description: >-
            Unique key to prevent duplicate orders (e.g.,
            order-{timestamp}-{random})
          schema:
            type: string
          example: order-1771523993-abc123
        - name: X-Timestamp
          in: header
          required: true
          description: >
            Unix timestamp in seconds (current time). Must be within 5 minutes
            of server time.

            Used to prevent replay attacks.
          schema:
            type: integer
          example: 1771523993
        - name: X-Signature
          in: header
          required: true
          description: >
            HMAC-SHA256 signature of the payload (timestamp + "." +
            request_body), base64-encoded.

            Sign using your HMAC secret from the Integrations page (Developer
            section).
          schema:
            type: string
          example: IqdgKXgloLzL5akDgFEwPaK6wviozf+3U0Q2eeevl494gpFUIdwMwALs+6bRn...
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - customer_email
                - customer_name
                - billing_address
                - shipping_address
                - subtotal
                - total_amount
                - payment_method
                - items
              properties:
                customer_id:
                  type: string
                  format: uuid
                  description: Customer UUID (optional - guest checkout supported)
                  example: c320094c-eb65-4879-804c-83d2e1dd7f99
                customer_email:
                  type: string
                  format: email
                  description: Customer email address. Required.
                  example: john.doe@example.com
                customer_name:
                  type: string
                  description: Customer full name. Required.
                  example: John Doe
                customer_phone:
                  type: string
                  description: Customer phone number (optional)
                  example: '+14155550199'
                billing_address:
                  type: object
                  description: Billing address. Required.
                  properties:
                    street:
                      type: string
                      example: 123 Main Street
                    city:
                      type: string
                      example: New York
                    state:
                      type: string
                      example: NY
                    zip:
                      type: string
                      example: '10001'
                    country:
                      type: string
                      example: US
                shipping_address:
                  type: object
                  description: Shipping address. Required.
                  properties:
                    street:
                      type: string
                      example: 350 Fifth Avenue
                    city:
                      type: string
                      example: New York
                    state:
                      type: string
                      example: NY
                    zip:
                      type: string
                      example: '10118'
                    country:
                      type: string
                      example: US
                items:
                  type: array
                  description: Order line items (at least one required)
                  minItems: 1
                  items:
                    type: object
                    description: >-
                      Provide **`variant_id` OR `product_id`** (at least one).
                      Prefer `variant_id` — it is the exact variant (SKU) the
                      shopper chose, so pass the same `variant_id` you put in
                      the cart straight through to the order. If you send only
                      `product_id`, the order is placed against the product's
                      **default** variant — convenient for single-variant
                      products, but for a product with multiple variants it may
                      not be the one the shopper selected. When both are sent,
                      `variant_id` wins (and `product_id` is resolved from it).
                    anyOf:
                      - required:
                          - variant_id
                      - required:
                          - product_id
                    properties:
                      variant_id:
                        type: string
                        format: uuid
                        description: >-
                          Variant (SKU) UUID. Preferred. Identifies the exact
                          variant to order. If omitted, the product's default
                          variant is used.
                        example: 7a1aa0b4-555c-3618-a48c-1415db27bcc8
                      product_id:
                        type: string
                        format: uuid
                        description: >-
                          Product UUID. Optional when `variant_id` is given (it
                          is resolved from the variant); when sent alone, the
                          product's default variant is ordered.
                        example: 715b8c10-eb11-4289-8c0e-2966e78cf9a5
                      product_name:
                        type: string
                        description: >-
                          Product name captured on the line item. Optional — if
                          omitted, it is resolved from the product
                          automatically.
                        example: Apple MacBook Pro
                      product_sku:
                        type: string
                        description: Product SKU
                        example: SKU-001
                      quantity:
                        type: integer
                        minimum: 1
                        description: Quantity to order
                        example: 2
                      unit_price:
                        type: number
                        format: float
                        description: Price per unit at time of order
                        example: 1000
                      total_price:
                        type: number
                        format: float
                        description: Total price for this line item (quantity × unit_price)
                        example: 2000
                      product_options:
                        type:
                          - object
                          - 'null'
                        description: Product variant options (e.g., color, size)
                        example:
                          color: Space Black
                          storage: 256GB
                payment_method:
                  type: string
                  description: Payment method identifier (required)
                  example: card
                  enum:
                    - card
                    - stripe
                    - paypal
                    - paystack
                    - mpesa
                    - cash
                payment_status:
                  type: string
                  description: Payment status (defaults to pending)
                  enum:
                    - pending
                    - paid
                    - failed
                    - refunded
                  example: pending
                order_status:
                  type: string
                  description: Order fulfillment status (defaults to pending)
                  enum:
                    - pending
                    - processing
                    - shipped
                    - delivered
                    - cancelled
                  example: pending
                subtotal:
                  type: number
                  format: float
                  description: Subtotal before tax and shipping. Required.
                  example: 2000
                tax_amount:
                  type: number
                  format: float
                  description: Tax amount
                  example: 320
                shipping_amount:
                  type: number
                  format: float
                  description: Shipping cost
                  example: 200
                discount_amount:
                  type: number
                  format: float
                  description: Discount amount
                  example: 0
                total_amount:
                  type: number
                  format: float
                  description: Total order amount (required)
                  example: 2520
                notes:
                  type: string
                  description: Additional order notes
                  example: Please deliver between 9 AM - 5 PM
                tracking_number:
                  type: string
                  description: Shipping tracking number (optional, usually set on PATCH)
                  example: 1Z999AA10123456784
                estimated_delivery:
                  type: string
                  format: date-time
                  description: Estimated delivery date and time (optional)
                  example: '2026-02-15T14:00:00Z'
                payment_reference:
                  type: string
                  description: >-
                    External payment reference (e.g., Stripe charge ID, M-Pesa
                    receipt)
                  example: ch_1NqFvE2eZvKYlo2C8Z3y4abc
                shipping_metadata:
                  type:
                    - object
                    - 'null'
                  description: >-
                    Shipping calculation details from /v1/shipping/calculate for
                    audit trail
                  properties:
                    fee:
                      type: number
                      example: 100
                    zone_name:
                      type:
                        - string
                        - 'null'
                      example: null
                    tier_name:
                      type:
                        - string
                        - 'null'
                      example: Local (within 10 km)
                    distance_meters:
                      type:
                        - number
                        - 'null'
                      example: 19.23
                    is_free:
                      type: boolean
                      example: false
                    reason:
                      type: string
                      example: 19 m away
                    applied_rule:
                      type: string
                      enum:
                        - zone
                        - distance
                        - default
                      example: distance
                gift_card_redemption:
                  type:
                    - object
                    - 'null'
                  description: Optional gift card to redeem towards this order
                  required:
                    - code
                    - amount
                  properties:
                    code:
                      type: string
                      description: Gift card code to redeem
                      example: V5G6-4N7H-9P2R-8W1S
                    amount:
                      type: number
                      format: float
                      description: Amount to redeem from the gift card
                      example: 1000
                promotion_usages:
                  type:
                    - array
                    - 'null'
                  description: >-
                    Promotion usages applied to this order (tracked when
                    payment_status is paid)
                  items:
                    type: object
                    required:
                      - promotion_id
                      - discount_amount
                    properties:
                      promotion_id:
                        type: string
                        format: uuid
                        description: ID of the promotion that was applied
                        example: 7a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e
                      discount_amount:
                        type: number
                        format: float
                        description: Discount amount attributed to this promotion
                        example: 150
                apply_store_credit:
                  type: boolean
                  description: >
                    Apply the customer's redeemable store credit to this order.
                    When

                    true (and the order has a `customer_id`), store credit is
                    spent

                    against the order total, capped at the total. The amount
                    actually

                    applied is returned as `store_credit_applied`. Requires a
                    customer.
                  example: true
                store_credit_amount:
                  type: number
                  format: float
                  description: >
                    Optional cap on how much store credit to apply. When omitted
                    (and

                    `apply_store_credit` is true) up to the full order total is
                    applied.

                    The applied amount never exceeds the available balance or
                    the total.
                  example: 25
                gclid:
                  type:
                    - string
                    - 'null'
                  description: >-
                    Google ad click id, when the shopper arrived from a Google
                    ad. On a paid order this lets the merchant's Google
                    advertising get credit for the sale. Forward the true
                    captured value; omit if not present.
                  example: Cj0KCQiA...gclid
                fbclid:
                  type:
                    - string
                    - 'null'
                  description: >-
                    Meta (Facebook & Instagram) ad click id, when the shopper
                    arrived from a Meta ad. On a paid order this lets the
                    merchant's Meta advertising get credit for the sale. The
                    conversion is sent server-side (Conversions API) and
                    de-duplicated against the storefront's Meta Pixel by the
                    order id, so it counts even when the browser blocks the
                    Pixel. Forward the true captured value; omit if not present.
                  example: IwAR1abc...fbclid
                ad_consent:
                  type:
                    - object
                    - 'null'
                  description: >-
                    Advertising privacy-consent state captured at checkout, used
                    to decide whether a conversion may be reported to an ad
                    platform for this shopper's region (required for EEA
                    shoppers; UK/Swiss records are not dropped for a missing
                    signal). Always forward the true captured values — never
                    guess.
                  properties:
                    ad_user_data:
                      type: string
                      enum:
                        - granted
                        - denied
                        - unspecified
                      example: granted
                    ad_personalization:
                      type: string
                      enum:
                        - granted
                        - denied
                        - unspecified
                      example: granted
                    jurisdiction:
                      type: string
                      description: The shopper's region bucket (e.g. EEA, UK, CH, US).
                      example: US
            example:
              customer_email: john.doe@example.com
              customer_name: John Doe
              customer_phone: '+12125550142'
              billing_address:
                street: 350 Fifth Avenue
                city: New York
                state: NY
                zip: '10118'
                country: US
              shipping_address:
                street: 350 Fifth Avenue
                city: New York
                state: NY
                zip: '10118'
                country: US
              items:
                - product_id: 715b8c10-eb11-4289-8c0e-2966e78cf9a5
                  variant_id: 15ca5a86-4b1b-47c3-8762-a1aa43941565
                  product_name: Apple MacBook Pro
                  product_sku: APP-MBP-13
                  quantity: 1
                  unit_price: 1999
                  total_price: 1999
              payment_method: card
              payment_status: paid
              subtotal: 1999
              shipping_amount: 5.99
              discount_amount: 0
              total_amount: 2004.99
              shipping_metadata:
                fee: 5.99
                tier_name: Local (within 10 km)
                distance_meters: 8548
                is_free: false
                reason: 8.5 km away
                applied_rule: distance
              notes: Leave with front desk
      responses:
        '201':
          description: >-
            Order created successfully (or existing order returned if
            idempotency key matches)
          headers:
            Tybrite-Environment:
              $ref: '#/components/headers/TybriteEnvironment'
          content:
            application/json:
              schema:
                type: object
                required:
                  - order
                properties:
                  order:
                    $ref: '#/components/schemas/Order'
                  store_credit_applied:
                    type: number
                    format: float
                    description: >
                      Present only when store credit was applied to this order.
                      The

                      amount of the customer's store credit spent against the
                      order.
                    example: 25
                  post_processing_warnings:
                    type: array
                    description: >
                      Optional. Present only when one or more post-order
                      processing

                      steps (gift card redemption, stock reduction, etc.)
                      failed.

                      The order itself was created successfully, but a
                      downstream

                      side effect needs human follow-up. Each warning indicates
                      the

                      stage that failed and a human-readable message.
                    items:
                      type: object
                      required:
                        - stage
                        - message
                      properties:
                        stage:
                          type: string
                          description: The post-processing stage that emitted the warning
                          example: gift_card_redemption
                        message:
                          type: string
                          description: Human-readable description of the failure
                          example: >-
                            Gift card redemption failed: Gift card not found for
                            this store. The order was created but no gift card
                            credit was applied. Please contact support.
              example:
                order:
                  id: 922d235e-48c9-4933-abcd-88cb2a264f86
                  order_number: ORD-1771523999910
                  customer_id: c320094c-eb65-4879-804c-83d2e1dd7f99
                  customer_email: john.doe@example.com
                  customer_name: John Doe
                  order_status: pending
                  payment_status: pending
                  total_amount: 2520
                  items:
                    - product_id: 715b8c10-eb11-4289-8c0e-2966e78cf9a5
                      product_name: Apple MacBook Pro
                      quantity: 2
                      unit_price: 1000
                      subtotal: 2000
                  created_at: '2026-02-19T10:30:00Z'
        '400':
          description: Invalid request (missing required fields, invalid data)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missing_total_or_payment_method:
                  summary: Missing required total_amount or payment_method
                  value:
                    error:
                      code: invalid_request
                      message: total_amount and payment_method are required
                missing_items:
                  summary: Empty or missing items array
                  value:
                    error:
                      code: invalid_request
                      message: At least one order item is required
                missing_item_ref:
                  summary: Item missing both variant_id and product_id
                  value:
                    error:
                      code: invalid_request
                      message: Each order item must include variant_id or product_id
                tax_exclusive_missing_breakdown:
                  summary: Store uses tax-exclusive pricing without subtotal/tax_amount
                  value:
                    error:
                      code: invalid_request
                      message: >-
                        Store uses tax-exclusive pricing. Please provide both
                        subtotal and tax_amount.
        '401':
          description: >-
            Unauthorized - Invalid or missing authentication credentials, or
            HMAC signature verification failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalid_api_key:
                  summary: Invalid API key
                  value:
                    error:
                      code: unauthorized
                      message: Invalid API key
                missing_api_key:
                  summary: Missing API key
                  value:
                    error:
                      code: unauthorized
                      message: Missing authorization header
                invalid_hmac_signature:
                  summary: Invalid HMAC signature
                  value:
                    error:
                      code: invalid_signature
                      message: Request signature verification failed
                missing_timestamp:
                  summary: Missing X-Timestamp header
                  value:
                    error:
                      code: invalid_signature
                      message: Request signature verification failed
                expired_timestamp:
                  summary: Expired timestamp (>5 minutes old)
                  value:
                    error:
                      code: invalid_signature
                      message: Request signature verification failed
                wrong_hmac_secret:
                  summary: Wrong HMAC secret used
                  value:
                    error:
                      code: invalid_signature
                      message: Request signature verification failed
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '429':
          $ref: '#/components/responses/RateLimitError'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - BearerAuth: []
components:
  headers:
    TybriteEnvironment:
      description: >
        Confirms which environment your API key resolved to. Use this to verify
        that test keys

        are hitting sandbox data and live keys are hitting production data.


        - `production` — requests are scoped to live data; real orders,
        customers, and payments

        - `sandbox` — requests are scoped to isolated test data; no real money
        is moved


        Present on every authenticated response, including error responses.
      schema:
        type: string
        enum:
          - production
          - sandbox
      example: sandbox
  schemas:
    Order:
      type: object
      properties:
        id:
          type: string
          format: uuid
        order_number:
          type: string
          example: ORD-2026-001234
        customer_id:
          type: string
          format: uuid
        customer_email:
          type: string
          format: email
        customer_phone:
          type: string
        customer_name:
          type: string
        order_status:
          type: string
          enum:
            - pending
            - processing
            - shipped
            - delivered
            - cancelled
          example: pending
        payment_status:
          type: string
          enum:
            - pending
            - paid
            - failed
            - refunded
          example: pending
        payment_method:
          type: string
          enum:
            - stripe
            - paypal
            - paystack
            - mpesa
            - cash
            - bank_transfer
          description: Method used for payment
          example: stripe
        payment_reference:
          type: string
        subtotal:
          type: number
          format: float
          example: 1999.98
        tax_amount:
          type: number
          format: float
          example: 159.99
        tax_breakdown:
          type:
            - array
            - 'null'
          description: >-
            Per-jurisdiction tax detail when tax was calculated automatically
            for the shipping destination (one entry per taxing jurisdiction).
            Null when a flat store rate was used.
          items:
            type: object
            properties:
              country:
                type: string
                example: GB
              region:
                type: string
                example: GB
              jurisName:
                type: string
                example: UNITED KINGDOM
              taxName:
                type: string
                example: Standard Rate
              rate:
                type: number
                format: float
                example: 0.2
              taxable:
                type: number
                format: float
                example: 100
              tax:
                type: number
                format: float
                example: 20
        tax_source:
          type:
            - string
            - 'null'
          enum:
            - automatic
            - fallback
            - manual
            - null
          description: >-
            How the tax figure was produced — `automatic` (jurisdiction-accurate
            calculation for the destination), `fallback` (automatic calculation
            was unavailable, so the store's configured rate was used), or
            `manual` (the store's configured rate).
          example: automatic
        shipping_amount:
          type: number
          format: float
          example: 15
        discount_amount:
          type: number
          format: float
          example: 0
        total_amount:
          type: number
          format: float
          example: 2174.97
        billing_address:
          $ref: '#/components/schemas/Address'
        shipping_address:
          $ref: '#/components/schemas/Address'
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
        notes:
          type: string
        tracking_number:
          type: string
        estimated_delivery:
          type: string
          format: date-time
        shipped_at:
          type: string
          format: date-time
        delivered_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        environment:
          type: string
          enum:
            - production
            - sandbox
          description: >-
            Whether this order was created in the live or test environment.
            Sandbox orders are isolated from production data.
          example: production
        shipping_metadata:
          type:
            - object
            - 'null'
          description: Complete shipping calculation details for audit trail
          properties:
            fee:
              type: number
              format: float
              description: Calculated shipping fee
              example: 100
            zone_name:
              type:
                - string
                - 'null'
              description: Delivery zone name (if zone-based pricing)
              example: CBD Zone
            tier_name:
              type:
                - string
                - 'null'
              description: Pricing tier name (if distance-based pricing)
              example: Within Manhattan
            distance_meters:
              type:
                - number
                - 'null'
              format: float
              description: Calculated distance from store (if distance-based)
              example: 19.23
            is_free:
              type: boolean
              description: Whether free delivery was applied
              example: false
            reason:
              type: string
              description: Human-readable explanation of fee
              example: 19 m away
            applied_rule:
              type: string
              enum:
                - zone
                - distance
                - default
              description: Which pricing system was applied
              example: distance
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: invalid_request
            message:
              type: string
              example: Missing required field
            details:
              type: string
              example: The 'email' field is required
            hint:
              type: string
              description: Additional guidance on how to resolve the error.
            available_stock:
              type: integer
              description: Current stock level (present on insufficient_stock errors).
            current_cart_quantity:
              type: integer
              description: Quantity already in cart (present on insufficient_stock errors).
            limit:
              type: integer
              description: >-
                Rate limit ceiling (present on rate_limited / quota_exceeded
                errors).
            remaining:
              type: integer
              description: >-
                Requests remaining in the current window (present on
                rate_limited / quota_exceeded errors).
            reset:
              type: integer
              description: >-
                Unix timestamp when the rate limit / quota window resets
                (present on rate_limited / quota_exceeded errors).
    Address:
      type: object
      properties:
        street:
          type: string
          example: 123 Main St
        city:
          type: string
          example: New York
        state:
          type: string
          example: NY
        zip:
          type: string
          example: '10001'
        country:
          type: string
          example: US
    OrderItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        product_id:
          type: string
          format: uuid
          description: Product UUID
        product_name:
          type: string
        product_sku:
          type: string
        quantity:
          type: integer
          example: 2
        unit_price:
          type: number
          format: float
          example: 999.99
        subtotal:
          type: number
          format: float
          example: 1999.98
  responses:
    ForbiddenError:
      description: Insufficient permissions - operation requires secret key
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: forbidden
              message: >-
                This operation requires a secret key. Publishable keys have
                read-only access.
    NotFoundError:
      description: Resource not found
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: not_found
              message: Resource not found
    ConflictError:
      description: >
        Conflict — the request could not be completed because it conflicts with
        the current state of a resource.

        Common causes:

        - Email already registered to another customer at this store

        - Item already exists in wishlist

        - Idempotency-Key reused with a different request body
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: conflict
              message: A customer with this email already exists for this store
    RateLimitError:
      description: >-
        Too many requests. Two distinct `429` codes: `rate_limited` (an abuse
        throttle — too many requests too fast; carries an `X-RateLimit-Scope:
        abuse` header and is NOT counted against your monthly quota) and
        `quota_exceeded` (your plan's monthly request allowance is reached).
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limited
              message: >-
                Too many requests for this API key. Please slow down and try
                again shortly.
              remaining: 0
              reset: 1706198400
    ServerError:
      description: Internal server error
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: server_error
              message: An unexpected error occurred
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >
        **API Key Authentication**
            
        Use your API key in the Authorization header:

        ```

        Authorization: Bearer tybrite_sk_live_YOUR_KEY

        ```
            
        **Key Types:**
            
        **Secret Keys (Server-Side Only):**

        - Format: `tybrite_sk_live_*` (production) or `tybrite_sk_test_*`
        (sandbox)

        - Full read/write access to all endpoints

        - ⚠️ NEVER expose in client-side code or public repositories

        - Required for: write operations, authentication, payment verification,
        AI recommendations
            
        **Publishable Keys (Client-Safe):**

        - Format: `tybrite_pk_live_*` (production) or `tybrite_pk_test_*`
        (sandbox)

        - Read-only access (GET requests only, plus POST semantic search)

        - ✅ Safe for client-side JavaScript, mobile apps, and public code

        - Allowed for: browsing products, search, CMS content, pricing queries
            
        **Endpoint-Specific Requirements:**

        - **Authentication endpoints** (`/v1/auth/*`): Secret key required

        - **Payment verification** (`POST /v1/payments/verify`): Secret key
        required

        - **AI Recommendations** (`POST /v1/recommendations`): Secret key
        required

        - **Semantic Search** (`POST /v1/search`): Both key types allowed
        (read-only operation)

        - **All write operations**: Secret key required

        - **All read operations**: Both key types allowed
            
        Using a publishable key for restricted operations returns 403 Forbidden.

````