> ## 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.

# Quote a multi-merchant checkout

> What `POST /v1/cart/checkout` will charge for this basket, per merchant, before the
shopper pays — including each merchant's shipping line. It takes the same body and runs
the same computation as the checkout, but holds no stock, creates no order and starts no
payment, so it can be called as the shopper edits their basket or their address.

Each merchant's items are priced for delivery separately: by that merchant's own delivery
rates for the destination, or by the marketplace's flat rate per merchant, and free when
the merchant's items reach the marketplace's free-shipping threshold. When the request
carries a street `shipping_address` and a `parcel` for a merchant in
`shipping_selections`, that merchant's carrier options are listed in `rates`; send the
chosen option's `rate_id` (and `rate_source`) to the checkout. Carrier options are not
offered for items the marketplace ships itself.

The quote never includes commission or what a merchant is paid.

Requires the marketplace operator key (a publishable key is enough).




## OpenAPI

````yaml /openapi.yaml post /v1/cart/checkout/quote
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_*`

    - Reads, plus the writes a shopper makes on their own behalf (cart,
    wishlist, reviews, events)

    - ✅ 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).
      
    ## Money and currency


    **Every amount in this API is in major currency units.** A price of `369.99`
    is three hundred and

    sixty-nine dollars and ninety-nine cents. This holds everywhere without
    exception — catalog prices,

    cart and order totals, payment amounts, refunds, gift-card balances and
    store credit.


    Never send minor units. Payment processors have their own conventions
    (Stripe counts in cents,

    Paystack in kobo) and Galactic Core converts to and from those internally,
    so a `100.00` payment

    reaches Stripe as `10000` without you doing anything. Sending `10000`
    yourself for a `100.00` order

    is rejected with `amount_mismatch` rather than charging a hundred times the
    intended amount.


    **Currency codes are ISO 4217 and case-insensitive.** `usd` and `USD` are
    equivalent on the wire;

    each is normalised to whatever the chosen processor expects.


    ## 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: Agent
    description: >
      Tools for an assistant shopping a store on a shopper's behalf: search by
      constraints, product

      dossiers, comparison, a landed-price quote, cart drafts, the shopper's own
      context behind their

      consent, and checkout intents that the shopper confirms before any order
      is placed.


      Every response carries `evidence` naming the operation each figure was
      read from, so an agent can

      cite its source rather than restate a number. Most tools are deterministic
      and cost what any

      storefront read costs. Two, `interpret` and `draftCartFromIntent`, run a
      model on the store's

      behalf and debit its Agent Compute credits; the rest never do.


      **Key type behavior:** a publishable key is enough for every tool except
      `GET /v1/agent/usage`,

      which needs a secret key. Tools that act on a shopper's own data also need
      exactly one shopper

      credential header (`x-auth-token`, `x-external-auth` or `x-idp-token`).


      Payment is never taken by an agent: a checkout intent becomes an order
      only when the shopper

      confirms it, and the payment itself is started with `POST
      /v1/payments/initialize`.
  - 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: Campaigns
    description: Campaigns — a discount with a budget behind it
  - 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/cart/checkout/quote:
    post:
      tags:
        - Marketplace
      summary: Quote a multi-merchant checkout
      description: >
        What `POST /v1/cart/checkout` will charge for this basket, per merchant,
        before the

        shopper pays — including each merchant's shipping line. It takes the
        same body and runs

        the same computation as the checkout, but holds no stock, creates no
        order and starts no

        payment, so it can be called as the shopper edits their basket or their
        address.


        Each merchant's items are priced for delivery separately: by that
        merchant's own delivery

        rates for the destination, or by the marketplace's flat rate per
        merchant, and free when

        the merchant's items reach the marketplace's free-shipping threshold.
        When the request

        carries a street `shipping_address` and a `parcel` for a merchant in

        `shipping_selections`, that merchant's carrier options are listed in
        `rates`; send the

        chosen option's `rate_id` (and `rate_source`) to the checkout. Carrier
        options are not

        offered for items the marketplace ships itself.


        The quote never includes commission or what a merchant is paid.


        Requires the marketplace operator key (a publishable key is enough).
      operationId: marketplaceCheckoutQuote
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
              properties:
                items:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - variant_id
                      - merchant_store_id
                      - quantity
                    properties:
                      product_id:
                        type: string
                        format: uuid
                      variant_id:
                        type: string
                        format: uuid
                      merchant_store_id:
                        type: string
                        format: uuid
                      quantity:
                        type: integer
                        minimum: 1
                currency:
                  type: string
                  example: USD
                customer_name:
                  type: string
                  description: Used as the recipient name on carrier options.
                shipping_address:
                  type: object
                  additionalProperties: true
                  description: >-
                    Where the order is going (`line1`, `line2`, `city`, `state`,
                    `postal_code`, `country`). Located to price shipping when
                    `shipping_destination` is omitted, and needed for carrier
                    options.
                shipping_destination:
                  $ref: '#/components/schemas/MarketplaceShippingDestination'
                shipping_selections:
                  type: array
                  items:
                    $ref: '#/components/schemas/MarketplaceShippingSelection'
                discounts:
                  type: array
                  description: The same per-merchant discounts the checkout accepts.
                  items:
                    type: object
                    required:
                      - merchant_store_id
                    properties:
                      merchant_store_id:
                        type: string
                        format: uuid
                      promotion_id:
                        type: string
                        format: uuid
                      gift_card_code:
                        type: string
            example:
              items:
                - variant_id: 22222222-2222-2222-2222-222222222222
                  merchant_store_id: 3f1a2b4c-1111-2222-3333-444455556666
                  quantity: 2
                - variant_id: 55555555-5555-5555-5555-555555555555
                  merchant_store_id: 7a8b9c0d-1111-2222-3333-444455556666
                  quantity: 1
              currency: USD
              shipping_address:
                line1: 350 5th Ave
                city: New York
                state: NY
                postal_code: '10118'
                country: US
              shipping_selections:
                - merchant_store_id: 3f1a2b4c-1111-2222-3333-444455556666
                  parcel:
                    length: '10'
                    width: '8'
                    height: '4'
                    distance_unit: in
                    weight: '2'
                    mass_unit: lb
      responses:
        '200':
          description: The basket's price, per merchant, including shipping.
          headers:
            Tybrite-Environment:
              $ref: '#/components/headers/TybriteEnvironment'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketplaceCheckoutQuote'
              example:
                currency: USD
                subtotal: 124
                discount_total: 0
                shipping_total: 5.99
                total_amount: 129.99
                sellers:
                  - merchant_store_id: 3f1a2b4c-1111-2222-3333-444455556666
                    merchandise_total: 80
                    shipping:
                      amount: 0
                      pricing: free
                      fulfilled_by: seller
                      is_free: true
                      free_threshold: 75
                      description: Free shipping
                      selected_rate_id: null
                      rates: []
                  - merchant_store_id: 7a8b9c0d-1111-2222-3333-444455556666
                    merchandise_total: 44
                    shipping:
                      amount: 5.99
                      pricing: seller_rate
                      fulfilled_by: seller
                      is_free: false
                      free_threshold: 75
                      description: Local (within 10 km)
                      selected_rate_id: null
                      rates: []
        '400':
          description: >-
            The basket is invalid (`invalid_request`, `insufficient_stock`,
            `currency_mismatch`, `discount_invalid`), a merchant's currency has
            no exchange rate in the marketplace (`item_currency_unsupported`),
            no location could be found for a merchant priced by location
            (`shipping_destination_required`), a merchant or the marketplace
            does not deliver to the address (`shipping_not_deliverable`), the
            marketplace has no exchange rate for a merchant's delivery-rate
            currency (`shipping_currency_unsupported`), or a chosen carrier
            option cannot be used (`shipping_rate_invalid`).
          headers:
            Tybrite-Environment:
              $ref: '#/components/headers/TybriteEnvironment'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: shipping_destination_required
                  message: >-
                    Add a shipping address (or shipping_destination) so shipping
                    can be priced.
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          description: >-
            The marketplace has not finished connecting its payment account
            (`provider_not_live`).
          headers:
            Tybrite-Environment:
              $ref: '#/components/headers/TybriteEnvironment'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: provider_not_live
                  message: >-
                    This marketplace has not finished connecting its payment
                    account.
        '429':
          $ref: '#/components/responses/RateLimitError'
        '500':
          $ref: '#/components/responses/ServerError'
        '502':
          description: Shipping could not be priced right now (`shipping_unavailable`).
          headers:
            Tybrite-Environment:
              $ref: '#/components/headers/TybriteEnvironment'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: shipping_unavailable
                  message: Shipping could not be priced right now. Please try again.
      security:
        - BearerAuth: []
components:
  schemas:
    MarketplaceShippingDestination:
      type: object
      description: >-
        Where the basket is going, used to price each merchant's delivery rates.
        Send coordinates, or a place name. When omitted, the `shipping_address`
        is located instead.
      properties:
        latitude:
          type: number
          minimum: -90
          maximum: 90
          example: 40.7589
        longitude:
          type: number
          minimum: -180
          maximum: 180
          example: -73.9851
        place_name:
          type: string
          maxLength: 300
          description: >-
            A place to locate when coordinates are not known, such as a city or
            a full address.
          example: New York, NY 10036, US
        country_code:
          type: string
          description: ISO 3166-1 alpha-2 country code narrowing `place_name`.
          example: US
    MarketplaceShippingSelection:
      type: object
      description: >-
        A choice for one merchant's delivery. `parcel` asks for that merchant's
        carrier options in a quote; `rate_id` chooses one of the options a quote
        returned. The amount is never sent: the option is checked again against
        the merchant's carrier account and charged at its current price.
      required:
        - merchant_store_id
      properties:
        merchant_store_id:
          type: string
          format: uuid
        parcel:
          type: object
          description: The parcel for this merchant's items, needed for carrier options.
          required:
            - length
            - width
            - height
            - distance_unit
            - weight
            - mass_unit
          properties:
            length:
              type: string
              example: '10'
            width:
              type: string
              example: '8'
            height:
              type: string
              example: '4'
            distance_unit:
              type: string
              example: in
            weight:
              type: string
              example: '2'
            mass_unit:
              type: string
              example: lb
        rate_id:
          type: string
          description: A `rate_id` from this merchant's `rates` in a quote.
        rate_source:
          type: string
          enum:
            - shippo
            - custom
          description: The `source` of the chosen rate, as the quote returned it.
    MarketplaceCheckoutQuote:
      type: object
      description: >-
        What a unified checkout will charge, per merchant, before the shopper
        pays.
      properties:
        currency:
          type: string
          example: USD
        subtotal:
          type: number
          description: The basket's merchandise before any discount.
          example: 130
        discount_total:
          type: number
          example: 0
        shipping_total:
          type: number
          description: Shipping across every merchant.
          example: 15
        total_amount:
          type: number
          description: >-
            What the checkout will charge before any wallet balance: merchandise
            after discounts plus shipping.
          example: 145
        sellers:
          type: array
          items:
            type: object
            properties:
              merchant_store_id:
                type: string
                format: uuid
              merchandise_total:
                type: number
                description: This merchant's items after every discount.
                example: 80
              shipping:
                $ref: '#/components/schemas/MarketplaceShippingLine'
    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).
    MarketplaceShippingLine:
      type: object
      description: One merchant's shipping line, as the shopper sees it.
      properties:
        merchant_store_id:
          type: string
          format: uuid
        amount:
          type: number
          description: What the shopper pays to deliver this merchant's items.
          example: 6
        pricing:
          type: string
          enum:
            - seller_rate
            - operator_rate
            - carrier_rate
            - flat
            - free
          description: >-
            What priced the line: the merchant's own delivery rates
            (`seller_rate`), the marketplace's own delivery rates when the
            marketplace ships the items (`operator_rate`), a carrier option the
            shopper chose (`carrier_rate`), the marketplace's flat rate
            (`flat`), or its free-shipping threshold (`free`).
          example: seller_rate
        fulfilled_by:
          type: string
          enum:
            - seller
            - marketplace
          description: Who ships these items.
          example: seller
        is_free:
          type: boolean
          example: false
        free_threshold:
          type:
            - number
            - 'null'
          description: >-
            The marketplace's free-shipping threshold: a merchant's items
            totalling at least this much ship free. Null when the marketplace
            has none.
          example: 75
        description:
          type:
            - string
            - 'null'
          description: The delivery rate or carrier option that priced this line.
          example: Local delivery
        selected_rate_id:
          type:
            - string
            - 'null'
          description: The carrier option charged, when the shopper chose one.
        rates:
          type: array
          description: >-
            Carrier options for this merchant (quote only), present when the
            request carried a street address and a `parcel` for this merchant
            and the merchant ships the items.
          items:
            $ref: '#/components/schemas/MarketplaceShippingRate'
    MarketplaceShippingRate:
      type: object
      description: One carrier option for a merchant's delivery.
      properties:
        rate_id:
          type: string
        provider:
          type: string
          example: USPS
        service:
          type:
            - string
            - 'null'
          example: Priority Mail
        amount:
          type: number
          example: 8.1
        currency:
          type: string
          example: USD
        estimated_days:
          type:
            - integer
            - 'null'
          example: 2
        source:
          type: string
          enum:
            - shippo
            - custom
  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
  responses:
    UnauthorizedError:
      description: Authentication failed - invalid or missing API key
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unauthorized
              message: Missing or invalid Authorization header
    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
    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.

````