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

# Add a message to a dispute

> Adds a message from the shopper to the dispute's conversation thread, so
the shopper can add detail or respond to the operator or merchant while the
dispute is being reviewed. Messages cannot be added once the dispute is
closed.

**Authentication:** API key **and** a customer session (`x-auth-token` or
`x-external-auth`).




## OpenAPI

````yaml /openapi.yaml post /v1/disputes/{id}/messages
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/disputes/{id}/messages:
    post:
      tags:
        - Disputes
      summary: Add a message to a dispute
      description: >
        Adds a message from the shopper to the dispute's conversation thread, so

        the shopper can add detail or respond to the operator or merchant while
        the

        dispute is being reviewed. Messages cannot be added once the dispute is

        closed.


        **Authentication:** API key **and** a customer session (`x-auth-token`
        or

        `x-external-auth`).
      operationId: addDisputeMessage
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The dispute id.
        - name: x-auth-token
          in: header
          required: false
          schema:
            type: string
          description: Customer session token. Provide this **or** `x-external-auth`.
        - name: x-external-auth
          in: header
          required: false
          schema:
            type: string
          description: Bring-your-own-auth assertion. Provide this **or** `x-auth-token`.
        - name: store_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: >-
            **Marketplace operator key only.** The merchant the shopper is
            acting at.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                message:
                  type: string
                  description: The message body.
            example:
              message: >-
                I've attached the tracking screenshot showing no movement for 10
                days.
      responses:
        '201':
          description: Message added successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      thread_id:
                        type: string
                        format: uuid
                      sent:
                        type: boolean
              example:
                data:
                  thread_id: 5f0d2a11-3c9e-4b7a-9d21-6d0f2c8b4a10
                  sent: true
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - BearerAuth: []
components:
  responses:
    BadRequestError:
      description: Invalid request - malformed data or missing required fields
      headers:
        Tybrite-Environment:
          $ref: '#/components/headers/TybriteEnvironment'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: invalid_request
              message: Invalid request parameters
              details: The 'email' field is required
    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
    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
    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
  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:
    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).
  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.

````