Skip to main content
The OrdersService class (accessed via client.orders) covers the order lifecycle: reserving stock at checkout, creating an order, retrieving and listing orders, and moving one through its payment and fulfilment states. Creating and updating an order additionally carry an HMAC signature over the request body, so the server can confirm the payload was not altered in transit.
Sandbox isolation: Orders created with a tybrite_sk_test_* key are stored in the sandbox environment and are never visible to production keys, and vice versa. Every response includes a Tybrite-Environment: sandbox | production header confirming which environment your key resolved to.

🔐 Security & Signing (HMAC)

To prevent tampering, replay attacks, and duplicate processing of updates, createOrder and updateOrder require both an HMAC-SHA256 signature and a mandatory Idempotency Key.

Signing Process

  1. Generate Timestamp: Get the current Unix timestamp in seconds.
  2. Prepare Payload: Concatenate the timestamp and the JSON request body with a dot: timestamp + "." + JSON_body.
  3. Generate Signature: Compute an HMAC-SHA256 signature of the payload using your HMAC Secret (found in Integrations → API keys).
  4. Base64 Encode: The resulting signature must be Base64 encoded.

Code Implementation (TypeScript/Node.js)

NEVER expose your HMAC Secret client-side. Signing should always occur on a secure backend server. Requests with missing or invalid signatures will return 401 Unauthorized.

Core Operations

An order and each of its line items carry a custom_fields array — fields the merchant defined themselves, such as a buyer’s purchase-order number. createOrder accepts values for them at checkout. See Custom Fields.

listOrders

Retrieve a paginated list of your store’s orders, newest first. Use this to build order dashboards, fulfillment queues, or customer order history — anywhere you need to browse orders without already knowing an order ID. The listing returns the order rows themselves. Line items, status notes and custom fields are fetched per order and belong to getOrder; asking for one of them in ?fields= here returns 400 naming the field rather than omitting it silently. The list returns order summaries without line items (to keep responses fast). When you need the full order including its items, call getOrder with the order’s id.
Reading orders requires a Secret Key. Publishable keys receive 403 Forbidden. Order data contains customer details, addresses, and payment status — it must never be exposed to browsers.
Parameters (all optional):
Paging through all results — keep calling with the returned cursor until has_more is false:
Response shape:

reserveStock

Optional. Holds stock for the items a customer is checking out so they can’t be sold to someone else while payment is in flight. Returns 201 with reservation_ids and an expiry. Use it when there’s a gap between “customer commits to buy” and “payment confirms” — card redirects, 3-D Secure, mobile-money prompts — so the stock is claimed before the customer pays. For instant-capture or synchronous flows you can skip it and call createOrder directly; Galactic Core still prevents stock from going negative at order time. Reserving is a stronger guarantee for async payments, not a mandatory step before every order. Reservations are all-or-nothing: if any item lacks available stock, nothing is reserved and you get 409 insufficient_stock. Each hold expires automatically (15 minutes by default), so an abandoned checkout lets the hold lapse without an explicit release call. Works with publishable or secret keys. Each request is bounded. With a publishable key it can hold at most 25 items and 25 units of any one variant; with a secret key, 100 items and 1,000 units of any one variant. Quantities for a variant that appears on several lines are added together. A request over a limit returns 400 and reserves nothing. Reservations are rate limited to 300 requests an hour per API key and, with a publishable key, 60 an hour per visitor. customer_id is recorded on the hold only when the call is made with a secret key; from a publishable key the hold is kept without a customer. A key issued through GC Connect needs the orders:write scope to reserve stock, and returns 403 without it.
The reserveStock call itself returns:
Response

createOrder

Place a new order. Returns 201 Created (semantic create) with the new order resource. Requires verified signatures and idempotency protection.
  • Retrying is safe. The first request with a given Idempotency-Key returns 201 with the order nested under an order property. Any later request carrying that same key — a retry, a double-submit, or two requests racing each other — returns 200 OK with the order that was already created, as a bare order resource (the same shape getOrder returns). Exactly one order is ever created for a key, so a client that retries on a timeout cannot double-order.
  • 202 Accepted under heavy load. Some deployments defer order writes during a sustained order surge to keep the storefront responsive — the order is accepted and created a moment later rather than synchronously. On those deployments, and only while under that load, createOrder returns 202 with { status: 'accepted', idempotency_key, message } and no order in the body. Treat it as success-in-progress: fetch the order once it exists by replaying createOrder with the same Idempotency-Key (which returns 200 with the order), or by polling getOrder if you have the id. Below that load — and on deployments that do not defer writes — creation is synchronous and returns 201 as above. A client that cannot handle a 202 (does not poll) should only be used against deployments where deferral is off.
  • Required body fields: customer_email, customer_name, billing_address, shipping_address (except on a pickup order), subtotal, total_amount, payment_method, and a non-empty items array of at most 100 lines (more answers 400). Each item must include variant_id or product_id (at least one).
  • variant_id vs product_id per item — prefer variant_id (the exact variant the shopper chose; pass the same one you put in the cart straight through). If you send only product_id, the order is placed against the product’s default variant — fine for single-variant products, but for a multi-variant product it may not be the one the shopper selected. Sending both is fine: variant_id wins and product_id is resolved from it. See Products and variants — which ID to send where for the full picture.
  • Optional: customer_id (guest checkout is supported — pass customer details without a customer_id), customer_phone, product_name per item (resolved from the product if omitted), tax_amount, shipping_amount, shipping_rate_id + shipping_rate_source (a carrier option from calculateShipping), shipping_option_id (a delivery or pickup option from calculateShipping), fulfillment_method + pickup_location_id (collection at one of the store’s locations), shipping_parcel (required with shipping_rate_source: custom), shipping_address_from, discount_amount, notes, gift_card_redemption, promotion_usages, and reservation_ids (from reserveStock — converts a checkout hold into the stock deduction instead of a fresh decrement). On a pending order the reservation ids are kept with the order: they are committed when the order is marked paid and released when it is cancelled or its payment fails. Only holds on the order’s own items are committed, and at most 100 ids are accepted.
  • currency — the ISO 4217 code the order is priced in. Defaults to the store’s default storefront currency. Set it when you present prices in a currency the shopper picked, so the order is recorded in the currency they actually paid in.
  • order_number — your own order reference. Galactic Core generates one when you omit it; pass your own to keep numbering aligned with a system you already run.
  • Attribution (optional): pass attribution to record where the sale came from, so it is credited to the right traffic source in the merchant’s reports: { session_id, utm_source, utm_medium, utm_campaign, utm_term, utm_content, referrer }, plus content_id + content_type to close the loop on a shopper who arrived from a blog post or lookbook. utm_term and utm_content carry the paid keyword and the creative, so a merchant can compare one ad variant against another rather than only one source against another. The same fields are also accepted at the top level of the body if you already send them that way. Promotions are recorded from the server-validated result rather than what you pass, so the record always reflects the discount the shopper actually received.
  • Tax: if you omit tax_amount, Galactic Core calculates it. When the store has automatic tax, it is calculated for the shipping_address, with the shipping charge rated as its own line, and returned as tax_amount with a per-jurisdiction tax_breakdown and a tax_source of automatic. Otherwise the store’s own rate applies — to the line totals, plus the shipping charge when the store charges tax on shipping — and a tax_amount you send must match that figure to the cent, or the order is refused with 400 price_mismatch. When the store’s prices include tax, the tax is taken from what the shopper pays and subtotal is the line totals minus that tax. If automatic calculation is unavailable the store’s rate is used (tax_source: fallback), so an order never fails on tax. TaxService.previewTax returns the same figure before checkout. See Order schema for the fields.
  • Shipping: shipping_amount is checked on every order, $0 included, and may not be negative.
    • With a carrier option (shipping_rate_id + shipping_rate_source), the option is fetched again from its source — the store’s carrier account, or its custom shipping extension re-quoted for shipping_address and shipping_parcel. An unknown, expired or foreign option, one in another currency, or a test-mode option on a live order returns 400 shipping_rate_invalid; an amount that differs from the option returns 400 price_mismatch. (shippo_rate_id is equivalent to shipping_rate_id with shipping_rate_source: shippo.)
    • Otherwise, when the store has delivery rates and the destination is known (shipping_latitude + shipping_longitude, or a shipping_address that can be located), the amount must equal the store’s charge for that address, with free-delivery thresholds measured against the line totals after discount_amount; a mismatch returns 400 price_mismatch, and a destination the store does not deliver to returns 400 shipping_not_deliverable.
    • A store with no delivery rates accepts the amount sent, as does an order whose destination cannot be located; such an order is recorded as not checked.
    • When shipping cannot be checked because the service is unreachable, the order is not created and the response is 502 shipping_unavailable.
  • Delivery options and pickup: calculateShipping returns options[] — the store’s delivery options and pickup locations. Pass the chosen option’s id as shipping_option_id.
    • Delivery (fulfillment_method: 'delivery', the default): place is standard delivery, priced as above; a named option (opt:<id>, such as Express) must be one the order qualifies for, at its own fee for the address and basket. An option the order does not qualify for returns 400 shipping_option_unavailable; an amount that differs from its fee returns 400 price_mismatch.
    • Pickup (fulfillment_method: 'pickup'): send the location’s id as pickup_location_id, or omit it to use the store’s primary pickup location. shipping_address is not needed — the location’s address is stored as the order’s shipping address, with type: 'pickup' — and tax is calculated at the location. shipping_amount must equal the location’s pickup fee, or the order is refused with 400 price_mismatch. A location that is not one of the store’s pickup locations, or an order below the location’s minimum, returns 400 pickup_unavailable.
  • Store credit: pass apply_store_credit: true (optionally with store_credit_amount to cap it) to spend the customer’s redeemable store-credit balance against this order. Requires a customer_id. The amount actually applied — capped at the order total and the available balance — is returned as store_credit_applied.
  • Ad conversion tracking (optional): if the shopper arrived from a paid ad, pass the click id — gclid (Google) and/or fbclid (Meta / Facebook & Instagram) — and their privacy-consent state as ad_consent: { ad_user_data, ad_personalization, jurisdiction } (each consent value 'granted' | 'denied' | 'unspecified'; jurisdiction is the shopper’s region, e.g. EEA/UK/CH/US). On a paid order this lets the merchant’s advertising get credit for the sale on each platform. Galactic Core records the consent state with the order and only reports the conversion to the ad platform where the shopper’s consent and region allow it — so always forward the true captured values (never guess). The Meta conversion is sent server-side and de-duplicated against the storefront’s Meta Pixel using the order id, so it counts even when the browser blocks the Pixel. A storefront should wire this alongside a consent banner for EEA, UK, and Swiss visitors. The click id is also kept on the order itself, so the merchant’s own reports can attribute revenue and profit to the ad that earned it rather than reading it back from the ad platform.
  • Missing a required field returns 400 with a message naming the field.
  • Replay Protection: The X-Timestamp must be within 5 minutes of the server time.
  • Post-processing: When payment_status: "paid" is set, Galactic Core applies gift card redemption, stock reduction, and customer metrics updates. See Post-Processing Warnings below — the response may include a post_processing_warnings array that you must check.
A pickup order names the location and the option instead of a shipping address:
Response
Because you pass an idempotencyKey, the SDK can safely auto-retry this call on a transient failure — a retry returns the original order rather than creating a duplicate.
createOrder returns { order, store_credit_applied?, post_processing_warnings? }. store_credit_applied is present only when store credit was applied. getOrder, updateOrder, and listOrders return the order resource(s) at the top level. An updateOrder that marks the order paid can also carry post_processing_warnings beside the order fields.

Post-Processing Warnings

When an order is created with payment_status: "paid", Galactic Core applies these follow-up steps:
  1. Gift card redemption (if gift_card_redemption provided)
  2. Stock reduction per item
  3. Customer metrics update (purchase count and lifetime value)
If any step fails, the order is still recorded — but the response includes a post_processing_warnings array. You MUST check this array and route any failures to a support or retry flow.

Response shape

Handling pattern

Customer metrics (total_purchases, lifetime value) update automatically when an order is paid. Counts stay accurate even when many orders for the same customer are placed at once — no action needed on your side.

getOrder

Retrieve complete details for a specific order.
Reading orders requires a Secret Key. Publishable keys receive 403 Forbidden. Order data contains customer PII, addresses, and payment status — it must not be exposed to browsers.
The response carries status_notes — customer-visible updates the merchant added as the order moved. These cover the stretch between payment and dispatch (preparation, checks, packing) that a shopper otherwise hears nothing about, so an order-status page reads as a timeline rather than a single status word. Oldest first. Internal notes are never included. How the order is received is on the order itself: fulfillment_method is delivery or pickup; pickup_location_id names the collection location on a pickup order; fulfillment_location_id is the store location the order is fulfilled from (the pickup location, or the location deliveries leave from when the store delivers from its nearest location); and shipping_option_id is the option the order was placed with. On a pickup order shipping_address is the location’s address, with type: 'pickup'. shipping_metadata.shipping_source records what the shipping charge was checked against — zone_tier, fallback, pickup, shippo, custom, not_configured or unverified — and shipping_metadata.shipping_rule the rule that priced it (zone, distance, fallback or pickup).
Response

updateOrder

Update fulfillment or payment status. Requires HMAC signature and Idempotency Key.
  • Idempotency: Use a unique key for each distinct update (e.g., update-payment-{order_id}-{timestamp}).
  • Protection: This prevents double-triggering side effects like accounting entries or inventory reduction on network retries — and lets the SDK auto-retry the call safely.
The full updated order is returned at the top level:
Response
Besides the two statuses, the body accepts notes, tracking_number, estimated_delivery, shipped_at, delivered_at, payment_reference (checked against the store’s payment method when marking an order paid), reservation_ids (stock already held for the shopper, committed rather than decremented again), and shipping_metadata — fulfilment details such as a purchased label’s carrier, service, label URL and tracking. shipping_metadata replaces the order’s stored object in full rather than being merged into it, so include any existing keys that should be kept:
Status changes follow a fixed set of transitions:
  • payment_status moves from pending to paid or failed, and from failed to paid. A paid order does not move back. refunded is set by processing a refund, not by updateOrder.
  • order_status moves forward only: pending → confirmed → processing → shipped → delivered. cancelled is available until the order ships. cancelled and delivered are final, and a cancelled order cannot be marked paid.
  • Sending a field with the value it already has is not a transition: the call succeeds and nothing is repeated, so marking an order paid twice runs the paid side effects once.
  • Any other move returns 409 invalid_transition. When two updates change the same order at once, one succeeds and the other returns 409 order_changed; fetch the order and decide whether to send the update again.
When an unpaid order is cancelled or its payment fails, its stock holds are released and any store credit applied to it is returned to the customer. If the update marked the order paid and a follow-up step did not complete, the response carries post_processing_warnings with the same { stage, message } entries described in Post-Processing Warnings. The order stays paid.

Security & Error Handling

Side Effects of payment_status: 'paid'

Transitioning an order to the paid state automatically triggers:
  1. Inventory Sync: Live stock reduction across variants.
  2. Accounting Engine: Double-entry bookkeeping entry creation.
  3. LTV Update: Increments customer lifetime value and purchase counts (accurate even under heavy concurrent order load).
  4. Promoter Logic: Marks loyalty points and promotion usage as finalized.

Response Codes