Skip to content

BigCommerce REST API Adapter

v2/v3 Compatible

Last updated: May 15, 2026

A drop-in compatible REST API for STOAR that mirrors BigCommerce v2 (/api/v2/orders) and v3 (/api/v3/catalog/products) — same URL paths, same RFC 2822 dates, same status_id numeric vocabulary, same { data, meta.pagination } envelope on v3, same X-Pagination-* headers on v2, same X-Auth-Token header. Existing BigCommerce client libraries (bigcommerce/api Node SDK, bigcommerce-api-php, BigCommerce Python) talk to STOAR with no modification.

This is the fourth implementation of STOAR's extensible API adapter framework (after Magento, WooCommerce, and Shopify). It demonstrates that the architecture handles two version-flavours under one adapter (v2 + v3) without duplicating any business logic.


Table of contents #


Quick start #

TOKEN="5|abcdef…"     # Sanctum personal-access token with bigcommerce:admin ability

# v2 — orders
curl -H "X-Auth-Token: $TOKEN" \
     "https://app.stoar.ai/api/v2/orders?limit=5" | jq '.[] | {id, status_id, status, total_inc_tax}'

# v2 — line items for a specific order (separate sub-endpoint)
curl -H "X-Auth-Token: $TOKEN" \
     "https://app.stoar.ai/api/v2/orders/10126/products" | jq '.[].name'

# v3 — catalog products with pagination meta envelope
curl -H "X-Auth-Token: $TOKEN" \
     "https://app.stoar.ai/api/v3/catalog/products?limit=2&page=1" | jq '.meta.pagination'

# v3 — single product
curl -H "X-Auth-Token: $TOKEN" \
     "https://app.stoar.ai/api/v3/catalog/products/789"

Bearer auth (-H "Authorization: Bearer $TOKEN") is also accepted as a fallback.


Authentication #

BigCommerce's canonical auth header is X-Auth-Token: <token>. STOAR's adapter accepts it and a Bearer fallback:

Method Format Use case
X-Auth-Token X-Auth-Token: <sanctum-token> Default for all BigCommerce client libraries
Bearer (STOAR extension) Authorization: Bearer <sanctum-token> Native Sanctum — interchangeable with the other adapters

Tokens are issued from STOAR's /manager/api-tokens page or programmatically via AdminUser::createToken('label', ['bigcommerce:admin']). A single token can carry every adapter's ability simultaneously (bigcommerce:admin + magento:admin + shopify:admin + woocommerce:admin).

The AuthTokenMiddleware runs before auth:sanctum and promotes X-Auth-Token into a Authorization: Bearer … header. From STOAR's perspective every BC request is a normal Sanctum-authenticated request.

OAuth client-credential flow (real BC apps go through /auth/load and /oauth2/token) is not supported.


Endpoints #

All paths are relative to /api. v2 endpoints live under /api/v2; v3 endpoints under /api/v3.

GET /api/v2/orders

List orders — flat array body + pagination headers.

Authorization: bigcommerce:admin. Query: see Query parameters.

Response:

HTTP/1.1 200 OK
X-Pagination-Total-Count: 150
X-Pagination-Page-Total:  3
Content-Type: application/json

[
  { /* order object — see Response shape */ }
]

Why headers, not envelope? This is BC v2's actual convention. v3 endpoints use the { data, meta } envelope (see catalog products below). Real BC client libraries handle both.


GET /api/v2/orders/{id}

Single order — bare object (NO envelope on v2).

Errors:

{ "status": 404, "title": "The order requested could not be found.", "type": "..." }

GET /api/v2/orders/{id}/products

Note — line items live at a SEPARATE endpoint, not embedded on the order resource. This is one of BC's bigger differences from Magento, WooCommerce, and Shopify (all of which inline line items).

Response: flat array of BC v2 line-item objects.

[
  {
    "id":              30219,
    "order_id":        10126,
    "product_id":      112238,
    "variant_id":      95589,
    "name":            "Widget",
    "sku":             "WID-1-A",
    "type":            "physical",
    "base_price":      299,
    "price_ex_tax":    299,
    "price_inc_tax":   299,
    "base_total":      897,
    "total_ex_tax":    897,
    "total_inc_tax":   897,
    "quantity":        3,
    "is_refunded":     false,
    "product_options": [],
    "..."
  }
]

GET /api/v3/catalog/products

List catalog products — v3 envelope with data + meta.pagination.

Query: see Query parameters.

Response (200):

{
  "data": [
    { /* product — see Response shape */ }
  ],
  "meta": {
    "pagination": {
      "total":         150,
      "count":         50,
      "per_page":      50,
      "current_page":  1,
      "total_pages":   3,
      "links": {
        "current": "https://.../api/v3/catalog/products?page=1&limit=50",
        "next":    "https://.../api/v3/catalog/products?page=2&limit=50"
      },
      "too_many":      false
    }
  }
}

links.previous is included on pages > 1; links.next on all but the last page.


GET /api/v3/catalog/products/{id}

Single product — v3 envelope with data (single object) + empty meta.

Response:

{
  "data": { /* product — see Response shape */ },
  "meta": {}
}

Query parameters #

Common (orders + products)

Param Default Purpose
limit 50 items per page (max 250)
page 1 page number, 1-indexed

v2 orders only

Param Example Effect
status_id 11 Numeric BC status code → translated to Stoar status
customer_id 42 Filter by customer
min_id / max_id 100 id range
min_date_created / max_date_created 2025-01-01T00:00:00 created_at range
sort id:desc <field>:<asc\|desc>. Fields: id, date_created, date_modified, total_inc_tax

v3 catalog products only

V3 supports BC's richer operator suffix syntax: id:in, price:min, etc.

Param Example Effect
id 42 exact id
id:in 1,2,3 id IN list
id:not_in 4,5 id NOT IN list
sku WID-1 exact sku
name Widget exact name
keyword widget LIKE substring on name
is_visible true / false translates to active / inactive
is_featured true / false filter on is_featured
categories 5 exact category id
price:min 10 price >=
price:max 100 price <=
sort name, -price, -date_created, id leading - flips direction

Unknown operators or fields are silently ignored — same as BC behaviour.


Field mapping (BigCommerce ↔ STOAR) #

Order (v2)

BC v2 field STOAR source Notes
id id numeric
status_id status (translated) numeric BC code (1, 11, 7, 3, 10, 5, 4) — see Status translation
status status (translated) string label ("Pending", "Awaiting Fulfillment", "Shipped", …)
customer_id customer_id ?? 0 guest orders get 0
date_created, date_modified created_at, updated_at RFC 2822 format (NOT ISO 8601)
date_shipped updated_at if status='shipped'/'delivered' empty string otherwise
currency_code, default_currency_code currency (uppercased) e.g. EUR
currency_exchange_rate always '1.0000000000' Stoar is single-currency
total_inc_tax total_amount NUMERIC float (NOT a string — that's V3 / WC behaviour)
total_ex_tax total_amount - tax_amount
total_tax tax_amount
subtotal_ex_tax subtotal
subtotal_inc_tax subtotal + tax_amount
subtotal_tax tax_amount
shipping_cost_ex_tax, shipping_cost_inc_tax, base_shipping_cost shipping_amount
discount_amount, coupon_discount discount_amount
payment_method first non-archived OrderPayment.gateway
payment_status 'captured' if payment_status='succeeded' empty otherwise
refunded_amount refunded_amount ?? 0
staff_notes admin_notes
customer_message customer_notes
billing_address billing_info JSON nested object — flat shape with street_1/street_2 keys
products sub-resource {url, resource} pointer to /orders/{id}/products
shipping_addresses sub-resource {url, resource} pointer (not implemented)
coupons sub-resource {url, resource} pointer (not implemented)
items_total sum(items.quantity)
items_shipped sum(items.quantity) if status='shipped'/'delivered', else 0
customer_locale always 'en' not stored
channel_id always 1 not modelled

Order line_item (v2 /products)

BC v2 field Stoar source
id OrderItem.id
order_id OrderItem.order_id
product_id OrderItem.product_id
variant_id OrderItem.variant_id
name, name_customer, name_merchant OrderItem.name
sku variant SKU first, falls back to product
type always 'physical'
base_price, price_ex_tax OrderItem.price
price_inc_tax price + (tax_amount / quantity)
base_total, total_ex_tax price * quantity
total_inc_tax (price * quantity) + tax_amount
total_tax OrderItem.tax_amount
quantity OrderItem.quantity
product_options[] one element with variant name (when not Default)
weight, width, height, depth always 0
is_refunded, quantity_refunded, refund_amount always false/0 (Stoar tracks refund at order-level only)

Catalog product (v3)

BC v3 field STOAR source Notes
id id
name, description, sku passthrough
type always 'physical' BC also has 'digital'; Stoar is physical-only
is_visible status === 'active' bool
availability 'available' if active else 'disabled' enum
is_featured is_featured bool
inventory_tracking 'product' for simple, 'variant' for configurable
inventory_level stock (sum of variants for configurable)
inventory_warning_level low_stock_threshold ?? 0
price price (raw) NUMERIC float
sale_price special_price ?? 0 float
calculated_price min(price, special_price) float
weight weight ?? 0 float
tax_class_id tax_class_id ?? 0
categories [category_id] array of single int (NOT nested objects)
base_variant_id first variant's id
images[] image_path + gallery_paths inline array
variants[] inline full variant objects
custom_url {url: "/{slug}", is_customized: false, create_redirect: false}
page_title, meta_description, meta_keywords meta_title, meta_description, []
date_created, date_modified created_at, updated_at ISO 8601 (v3 uses ISO; v2 uses RFC 2822)
condition always 'New' not modelled
total_sold, view_count, reviews_rating_sum, reviews_count always 0 not aggregated
related_products always [-1] BC convention for "auto-related"
brand_id always null Stoar has no brand model

Catalog product variant (v3)

BC v3 field Stoar source
id Variant.id
product_id Variant.product_id
sku Variant.sku
price, calculated_price Variant.price
inventory_level Variant.stock
weight, calculated_weight Variant.weight
purchasing_disabled !Variant.is_active
image_url Variant.image_path ?? ""
option_values[] always [] (Stoar variant attributes use a different model)

Status translation #

BigCommerce uses NUMERIC status_id codes plus a parallel string status field. The translator decomposes Stoar's enum into both.

Stoar → BigCommerce (rendering)

Stoar status status_id status (string)
pending 1 Pending
paid 11 Awaiting Fulfillment
processing 7 Awaiting Pickup
shipped 3 Shipped
delivered 10 Completed
cancelled 5 Cancelled
refunded 4 Refunded

BigCommerce filter → Stoar (parsing)

?status_id=N is translated using the same lookup table. Codes that don't match anything in the map (e.g. BC's status 2 = "Manually Verified" or 6 = "Declined") fall through silently — matching BC's "no exact equivalent" behaviour. Real BC's full code map has ~14 entries; STOAR exposes the seven that map cleanly.

Product status

BigCommerce has no string status field — it uses is_visible (bool) + availability enum. Filter via ?is_visible=true for active products, ?is_visible=false for inactive.


Response shape #

v2 order — annotated

{
  "id":             456,
  "customer_id":    45,
  "date_created":   "Tue, 03 Jun 2025 04:56:43 +0000",
  "date_modified":  "Tue, 03 Jun 2025 04:56:43 +0000",
  "date_shipped":   "",
  "status_id":      11,
  "status":         "Awaiting Fulfillment",
  "subtotal_ex_tax":   100,
  "subtotal_inc_tax":  110,
  "subtotal_tax":      10,
  "total_ex_tax":      105,
  "total_inc_tax":     115,
  "total_tax":         10,
  "items_total":       2,
  "items_shipped":     0,
  "payment_method":    "stripe",
  "payment_status":    "captured",
  "refunded_amount":   0,
  "currency_code":     "EUR",
  "currency_exchange_rate":   "1.0000000000",
  "default_currency_code":    "EUR",
  "billing_address": {
    "first_name":   "Jane",
    "last_name":    "Doe",
    "company":      "",
    "street_1":     "1 Test St",
    "street_2":     "",
    "city":         "Berlin",
    "state":        "",
    "zip":          "10115",
    "country":      "Germany",
    "country_iso2": "DE",
    "phone":        "+49…",
    "email":        "[email protected]",
    "form_fields":  []
  },
  "products": {
    "url":      "https://.../api/v2/orders/456/products",
    "resource": "/orders/456/products"
  },
  "shipping_addresses": {
    "url":      "https://.../api/v2/orders/456/shippingaddresses",
    "resource": "/orders/456/shippingaddresses"
  },
  "coupons": {
    "url":      "https://.../api/v2/orders/456/coupons",
    "resource": "/orders/456/coupons"
  },
  "store_default_currency_code": "EUR",
  "channel_id":     1,
  "..."
}

v3 catalog product — abbreviated

{
  "data": {
    "id":                  789,
    "name":                "Widget",
    "type":                "physical",
    "sku":                 "WID-1",
    "description":         "<p>A great widget</p>",
    "weight":              0.5,
    "price":               99.99,
    "sale_price":          0,
    "retail_price":        0,
    "calculated_price":    99.99,
    "categories":          [5],
    "is_visible":          true,
    "is_featured":         true,
    "availability":        "available",
    "inventory_tracking":  "product",
    "inventory_level":     42,
    "page_title":          "Widget – buy now",
    "custom_url": {
      "url":             "/widget",
      "is_customized":   false,
      "create_redirect": false
    },
    "images": [
      { "id": 0, "image_file": "products/widget.webp", "is_thumbnail": true, "sort_order": 0, "url_thumbnail": "products/widget.webp", "url_zoom": "products/widget.webp", "..." }
    ],
    "variants": [
      { "id": 12, "product_id": 789, "sku": "WID-RED", "price": 99.99, "inventory_level": 22, "purchasing_disabled": false, "..." }
    ]
  },
  "meta": {}
}

Real-world example #

GET /api/v2/orders/10126 against production. Same Stoar order featured in Magento, WooCommerce, and Shopify — rendered in BC v2 shape. PII anonymised.

{
  "id":                 10126,
  "customer_id":        5794,
  "date_created":       "Tue, 03 Jun 2025 04:56:43 +0000",
  "date_modified":      "Tue, 03 Jun 2025 04:56:43 +0000",
  "date_shipped":       "",
  "status_id":          11,
  "status":             "Awaiting Fulfillment",
  "subtotal_ex_tax":    936.98,
  "subtotal_inc_tax":   936.98,
  "subtotal_tax":       0,
  "shipping_cost_ex_tax":  0,
  "shipping_cost_inc_tax": 0,
  "shipping_cost_tax":     0,
  "total_ex_tax":       936.98,
  "total_inc_tax":      936.98,
  "total_tax":          0,
  "items_total":        5,
  "items_shipped":      0,
  "payment_method":     "payid",
  "payment_provider_id":null,
  "payment_status":     "captured",
  "refunded_amount":    0,
  "order_is_digital":   false,
  "currency_id":        1,
  "currency_code":      "USD",
  "currency_exchange_rate":   "1.0000000000",
  "default_currency_id":      1,
  "default_currency_code":    "USD",
  "staff_notes":        "",
  "customer_message":   "",
  "discount_amount":    0,
  "coupon_discount":    0,
  "shipping_address_count": 1,
  "is_deleted":         false,
  "ebay_order_id":      "0",
  "cart_id":            null,
  "billing_address": {
    "first_name":   "Jane",
    "last_name":    "Doe",
    "company":      "",
    "street_1":     "1 Example Street",
    "street_2":     "",
    "city":         "Phoenix",
    "state":        "AZ",
    "zip":          "85001",
    "country":      "United States",
    "country_iso2": "US",
    "phone":        "+1-555-0100",
    "email":        "",
    "form_fields":  []
  },
  "products": {
    "url":      "https://app.stoar.ai/api/v2/orders/10126/products",
    "resource": "/orders/10126/products"
  },
  "shipping_addresses": {
    "url":      "https://app.stoar.ai/api/v2/orders/10126/shippingaddresses",
    "resource": "/orders/10126/shippingaddresses"
  },
  "coupons": {
    "url":      "https://app.stoar.ai/api/v2/orders/10126/coupons",
    "resource": "/orders/10126/coupons"
  },
  "store_default_currency_code": "USD",
  "store_default_to_transactional_exchange_rate": "1.0000000000",
  "custom_status":      "Awaiting Fulfillment",
  "channel_id":         1,
  "ip_address":         "",
  "ip_address_v6":      "",
  "geoip_country":      "",
  "geoip_country_iso2": "",
  "is_email_opt_in":    false,
  "credit_card_type":   null,
  "order_source":       "www",
  "external_source":    null,
  "external_id":        null,
  "external_merchant_id":null,
  "tax_provider_id":    "",
  "customer_locale":    "en",
  "external_order_id":  ""
}

Caveats integrators should know

Field Observed Why
date_created, date_modified RFC 2822 string v2 specifically uses RFC 2822 (Tue, 03 Jun 2025 04:56:43 +0000). v3 endpoints use ISO 8601 — the inconsistency is BC's, we mirror it.
payment_method "payid" Stoar's gateway-key — not a BC-canonical method name.
payment_status "captured" for paid orders BC has its own status vocabulary; we map succeededcaptured.
total_inc_tax, total_ex_tax, etc. floats (e.g. 936.98) BC v2 renders money as floats — distinct from WooCommerce / Shopify which use 2-dp strings.
country "United States" Long name, derived from ISO-2 via a small lookup table (DE/US/GB/AU/AT/CH). Other countries return "".
products, shipping_addresses, coupons {url, resource} sub-resource pointers BC convention for "follow this URL to fetch related data". /orders/{id}/products is implemented; the others are not yet available.
customer_locale always "en" not stored on Stoar Customer.
channel_id always 1 Stoar is single-store; BC supports multi-channel.
currency_exchange_rate always "1.0000000000" Stoar single-currency.
staff_notes, customer_message admin_notes, customer_notes BC's terminology.
external_*, ebay_order_id, cart_id empty / null / "0" Stoar has no marketplace integration.

Error shape #

BigCommerce uses an RFC 7807-style envelope:

{
  "status": 404,
  "title":  "The order requested could not be found.",
  "type":   "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes",
  "errors": { "<field>": "..." }
}

type always points to BC's status-codes documentation page. errors is only present on validation failures (400).

HTTP Trigger title
400 bad query / validation Invalid query parameters. (with errors map)
401 missing or invalid token Not authenticated.
403 wrong-ability token Insufficient OAuth scope.
404 unknown resource The {resource} requested could not be found.
422 precondition failed dynamic message
429 rate-limit exceeded Too many requests. (with X-Rate-Limit-Time-Reset-Ms and X-Rate-Limit-Requests-Left headers)
500 unexpected server error Internal Server Error

Rate limiting #

The same api-rest Sanctum-token-keyed throttle that protects all other adapters protects BC routes. Configure both the threshold and the master switch at /manager/api-settings. When triggered, the response uses BC's RFC 7807 envelope:

HTTP/1.1 429 Too Many Requests
X-Rate-Limit-Time-Reset-Ms: 60000
X-Rate-Limit-Requests-Left: 0
Content-Type: application/json

{
  "status": 429,
  "title":  "Too many requests.",
  "type":   "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes"
}

The X-Rate-Limit-* headers match BC's documented behaviour. Real BC returns more headers and uses second-resolution windows; STOAR uses Laravel's minute window — close enough for client-library compatibility.

A single Sanctum token used across all four adapters shares one bucket — see Magento's Rate limiting section for the configuration UI walkthrough.


See also #

  • app/Api/Adapters/BigCommerce/Support/StatusTranslator.php — bidirectional vocabulary mapping
  • BigCommerce REST API reference — the upstream spec this adapter mirrors