API reference

OrderCore API Guide

REST API for order creation, sync, and webhooks. Authenticate with an API key, create idempotent orders, and receive signed webhooks. The full first-order flow is three calls: GET /healthPOST /v1/onboarding/demo-dataPOST /v1/orders.

This guide covers the public API surface only. Gateway plugin packages: /downloads. New here? Start with get started.

Base URLs

  • Production: https://api.ordercore.ai
  • Demo: https://demo-api.ordercore.ai

Authentication

Send your API key on every request:

  • X-API-Key: <your_key>
  • or Authorization: Bearer <your_key>
curl -H "X-API-Key: oc_live_..." https://api.ordercore.ai/v1/account/auth

Idempotency

For create operations include:

  • Header: Idempotency-Key
  • or body field: idempotency_key

Pagination

List endpoints accept page and per_page (default 20, max 100).

Create your first order (First Success)

Use this same flow everywhere (docs, tutorial, and get-started):

  1. Get API key: GET /health
  2. Create your first order: POST /v1/onboarding/demo-data
  3. Confirm order → trigger webhook: POST /v1/orders

After this flow you will have:

  • a real order in the system
  • a working API call
  • a clear go/no-go decision

Built for real systems:

  • idempotent order creation
  • retry-safe APIs
  • webhook-driven integrations

No store, no UI, no checkout flow needed. Just API.

You need an oc_live_... API key for this flow — it arrives with the 3-day trial, or request a first key without checkout.
export API_KEY="oc_live_..."

# 1) Get API key + health preflight
curl -sS https://api.ordercore.ai/health

# 2) Create your first order
SEED_JSON="$(curl -sS -X POST https://api.ordercore.ai/v1/onboarding/demo-data \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}')"

# 3) Confirm order → trigger webhook
CUSTOMER_ID="$(printf '%s' "$SEED_JSON" | jq -r '.sample_customer_id // empty')"
SKU_ID="$(printf '%s' "$SEED_JSON" | jq -r '.sample_order_item.sku_id // empty')"

test -n "$CUSTOMER_ID" || { echo "No sample_customer_id returned by onboarding"; exit 1; }
test -n "$SKU_ID" || { echo "No sample_order_item.sku_id returned by onboarding"; exit 1; }

# 4) create first retry-safe order and verify webhook delivery
curl -sS -X POST https://api.ordercore.ai/v1/orders \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-order-001" \
  -d "{\"customer_id\":\"$CUSTOMER_ID\",\"items\":[{\"sku_id\":\"$SKU_ID\",\"quantity\":1}]}"
Requires jq for onboarding response parsing.

Endpoints

GET /health – status

GET /direct-ai/manifest – public direct AI checkout manifest (commercial + technical entry points)

GET /direct-ai/browser-config – public browser-safe checkout config (Stripe publishable key when configured)

/ai-integrations – public AI integrations hub with OpenAI, Claude, Gemini, DeepSeek, and Grok packs plus free-tier and billing model summary

GET /integrations/catalog – public add-ons catalog (connectors, UCP compatibility summary)

GET /integrations/shopify/status – public Shopify install diagnostics (non-sensitive)

POST /v1/account/checkout-access-tokens – mint short-lived public checkout token

POST /v1/account/buyer-checkout-links – create branded buyer checkout link scoped to one SKU + quantity, with optional merchant_reference_id, success_url, and cancel_url (merchant_reference_id: max 128 chars, allowed [a-zA-Z0-9._:-])

POST /v1/account/buyer-checkout-links/regenerate – mint a fresh branded buyer link from a previous buyer URL or token while keeping the same scope

POST /ucp/public/checkout/sessions/{session_id}/payment-intent – create Stripe PaymentIntent for buyer confirmation

GET /v1/products – list products (filters: status)

POST /v1/products – create product

GET /v1/products/{product_id} – product detail

PATCH /v1/products/{product_id} – update product

DELETE /v1/products/{product_id} – delete product

GET /v1/skus/{skuCode} – SKU detail

GET /v1/inventory – list inventory (filters: sku_id/sku_code, location_id)

POST /v1/inventory/adjust – adjust stock

POST /v1/inventory/reserve – reserve stock

POST /v1/inventory/release – release reserved stock

GET /v1/prices – list prices (filters: sku_id/sku_code, price_list_code, currency, customer_id, quantity, valid_now, active_only)

GET /v1/orders – list orders

GET /v1/orders/lookup?merchant_reference_id=... – lookup latest checkout session and order by merchant reference

GET /v1/orders/{order_id} – order detail

GET /v1/account/auth – verify authenticated tenant and key context (auth mode, scopes, rate limit). Response includes api_key_scopes (always an array), rate_limit_per_minute, and key expiry metadata (api_key_expires_at, api_key_expires_in_days, api_key_expiry_status) when the key has expiry.

GET /v1/account/status – tenant plan and key status

GET /v1/account/usage – monthly usage and quota projection

GET /v1/account/readiness – go-live checklist for key access + TTL stability, catalog/inventory, active checkout.completed endpoint, successful delivery without recent abandoned webhook failures, and order flow

POST /v1/onboarding/catalog-csv – import a starter catalog, prices, and inventory without Shopify or another connector

GET /v1/integrations/shopify/status – Shopify integration status for current tenant

GET /v1/integrations/shopify/stores – connected Shopify stores

GET /v1/integrations/shopify/sync-logs – latest Shopify-origin order sync events

GET /v1/integrations/shopify/webhook-events – webhook processing events (filters: status, shop_domain)

Create Order

POST /v1/orders

{
  "customer_id": "cust_123",
  "items": [
    {"sku_id": "sku_123", "quantity": 2}
  ],
  "idempotency_key": "your-idempotency-key"
}
{
  "id": "order_...",
  "status": "pending",
  "created_at": "2026-02-09T12:34:56Z",
  "order_number": "OC-000123"
}

Public Direct Checkout Reference

Preferred direct-AI buyer flow is tokenized public checkout with Stripe PaymentIntent confirmation:

  1. POST /v1/account/checkout-access-tokens
  2. POST /ucp/public/checkout/sessions
  3. PUT /ucp/public/checkout/sessions/{session_id}
  4. POST /ucp/public/checkout/sessions/{session_id}/payment-intent
  5. Confirm client_secret in Stripe.js / Payment Element
  6. POST /ucp/public/checkout/sessions/{session_id}/complete with payment_data.payment_intent_id
Repo reference flow: scripts/public_checkout_flow.sh. It runs token issuance, public session setup, and PaymentIntent creation, then prints the final complete call to use after client-side confirmation.

Branded buyer page: /direct-ai-buyer-checkout

Browser-side operator reference: /direct-ai-reference-client

Browser config endpoint: https://api.ordercore.ai/direct-ai/browser-config

Scoped buyer link behavior: one branded buyer link creates at most one checkout session; reopening the same link resumes that session instead of minting a new one until checkout is completed. After completion, the same link returns 410 buyer_link_consumed. If you pass email, the link is locked to that buyer email. Every link also carries a signed merchant_reference_id for lead-to-order correlation.

{
  "sku_id": "sku_123",
  "quantity": 1,
  "currency": "USD",
  "merchant_reference_id": "lead-123",
  "email": "buyer@example.com",
  "success_url": "https://merchant.example/thank-you",
  "cancel_url": "https://merchant.example/cart",
  "full_name": "Jane Doe"
}
Unified helpers in repo: scripts/ordercore_merchant_helper.py, scripts/ordercore_merchant_helper.mjs, and scripts/ordercore_merchant_sdk.mjs. Python and Node CLIs now expose the same command-spec and machine-readable help shape. The SDK exports the same operations for direct import into merchant services.

Create flow: python3 scripts/ordercore_merchant_helper.py buyer-link-create --sku-id sku_123 --url-only

CSV row flow: python3 scripts/ordercore_merchant_helper.py buyer-link-create --catalog-csv ./catalog.csv --from-csv-row 1 --url-only

Batch flow: python3 scripts/ordercore_merchant_helper.py buyer-links-batch-from-csv --catalog-csv ./catalog.csv --start-row 1 --end-row 10 --output csv

Regenerate flow: python3 scripts/ordercore_merchant_helper.py buyer-link-regenerate --buyer-checkout-url https://ordercore.ai/direct-ai-buyer-checkout?token=... --url-only

Lookup flow: python3 scripts/ordercore_merchant_helper.py orders-lookup --merchant-reference-id lead-123 --output csv

Polling-safe lookup: python3 scripts/ordercore_merchant_helper.py orders-lookup --merchant-reference-id lead-123 --allow-missing

Auth preflight: python3 scripts/ordercore_merchant_helper.py account-auth --output json

Readiness preflight: python3 scripts/ordercore_merchant_helper.py account-readiness --output json

Go-live preflight: python3 scripts/ordercore_merchant_helper.py go-live-preflight --event-type checkout.completed --window-hours 24 --output json

Webhook ops: python3 scripts/ordercore_merchant_helper.py webhook-endpoints-list

Webhook smoke: python3 scripts/ordercore_merchant_helper.py webhook-smoke-trigger --mode retryable

Bootstrap wizard: bash scripts/ordercore_quick_setup.sh

Connector wizard: bash scripts/ordercore_connector_quick_setup.sh --platform shopify

Public path chooser: /get-started

Python JSON help: python3 scripts/ordercore_merchant_helper.py help --command webhook-metrics --format json

Node variant: node scripts/ordercore_merchant_helper.mjs webhook-metrics --event-type checkout.completed --window-hours 24 --output csv

Node readiness preflight: node scripts/ordercore_merchant_helper.mjs account-readiness --output json

Node go-live preflight: node scripts/ordercore_merchant_helper.mjs go-live-preflight --event-type checkout.completed --window-hours 24 --output json

Node JSON help: node scripts/ordercore_merchant_helper.mjs help --command webhook-metrics --format json

Merchant snippets

curl -sS -X POST https://api.ordercore.ai/v1/account/buyer-checkout-links \
  -H "X-API-Key: oc_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "sku_id": "sku_123",
    "quantity": 1,
    "currency": "USD",
    "merchant_reference_id": "lead-123",
    "email": "buyer@example.com",
    "success_url": "https://merchant.example/thank-you",
    "cancel_url": "https://merchant.example/cart"
  }'
const response = await fetch("https://api.ordercore.ai/v1/account/buyer-checkout-links", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ORDERCORE_API_KEY,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    sku_id: "sku_123",
    quantity: 1,
    currency: "USD",
    merchant_reference_id: "lead-123",
    email: "buyer@example.com",
    success_url: "https://merchant.example/thank-you",
    cancel_url: "https://merchant.example/cart"
  })
});

const data = await response.json();
console.log(data.buyer_checkout_url);
payload := strings.NewReader(`{"sku_id":"sku_123","quantity":1,"currency":"USD","merchant_reference_id":"lead-123","email":"buyer@example.com","success_url":"https://merchant.example/thank-you","cancel_url":"https://merchant.example/cart"}`)
req, _ := http.NewRequest(http.MethodPost, "https://api.ordercore.ai/v1/account/buyer-checkout-links", payload)
req.Header.Set("X-API-Key", os.Getenv("ORDERCORE_API_KEY"))
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var body struct {
  BuyerCheckoutURL string `json:"buyer_checkout_url"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body.BuyerCheckoutURL)
curl -sS -X POST https://api.ordercore.ai/v1/account/buyer-checkout-links/regenerate \
  -H "X-API-Key: oc_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "buyer_checkout_url": "https://ordercore.ai/direct-ai-buyer-checkout?token=...",
    "ttl_minutes": 15
  }'
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py buyer-link-regenerate \
  --buyer-checkout-url "https://ordercore.ai/direct-ai-buyer-checkout?token=..." \
  --url-only
{
  "payment_data": {
    "handler_id": "card",
    "payment_intent_id": "pi_12345"
  }
}
curl -sS "https://api.ordercore.ai/v1/orders/lookup?merchant_reference_id=lead-123" \
  -H "X-API-Key: oc_live_xxx"
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py orders-lookup \
  --merchant-reference-id lead-123 \
  --output csv
python3 scripts/ordercore_merchant_helper.py help --format json
python3 scripts/ordercore_merchant_helper.py help \
  --command webhook-metrics \
  --format json
ORDERCORE_API_KEY=oc_live_xxx \
node scripts/ordercore_merchant_helper.mjs orders-lookup \
  --merchant-reference-id lead-123 \
  --allow-missing \
  --output jsonl
node scripts/ordercore_merchant_helper.mjs help --format json
node scripts/ordercore_merchant_helper.mjs help \
  --command webhook-metrics \
  --format json
import { ordersLookup, webhookMetrics, resolveApiKey } from "./scripts/ordercore_merchant_sdk.mjs";

const apiKey = resolveApiKey({ env: process.env });
const lookup = await ordersLookup({
  apiKey,
  merchantReferenceId: "lead-123",
  allowMissing: true,
});
const metrics = await webhookMetrics({
  apiKey,
  eventType: "checkout.completed",
  windowHours: 24,
});

console.log({ lookup, metrics });

Use --allow-missing when your merchant workflow polls before checkout or order creation exists yet. In that mode the helper prints {"status":"not_found"} and exits successfully.

Webhook Endpoint Ops

ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-endpoints-list
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-endpoints-create \
  --url https://example.com/webhooks/ordercore \
  --events checkout.completed
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-endpoints-delete \
  --endpoint-id 123e4567-e89b-12d3-a456-426614174000

Delete is a deactivation operation. Delivery history stays queryable after endpoint retirement.

ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-deliveries-list \
  --event-type checkout.completed \
  --limit 10
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-deliveries-list \
  --event-type checkout.completed \
  --limit 10 \
  --output jsonl
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-metrics \
  --event-type checkout.completed \
  --window-hours 24
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-metrics \
  --event-type checkout.completed \
  --window-hours 24 \
  --output csv
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-smoke-trigger \
  --mode standard
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-smoke-trigger \
  --mode retryable

Catalog CSV Import

Use POST /v1/onboarding/catalog-csv when you want a self-serve starting catalog without Shopify, WooCommerce, or another platform connector.

Required CSV headers:

  • product_name
  • sku_code

Optional headers:

  • product_description
  • product_external_id or product_ref
  • sku_name
  • unit_price or price
  • quantity_on_hand or stock_qty
curl -sS -X POST https://api.ordercore.ai/v1/onboarding/catalog-csv \
  -H "X-API-Key: oc_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "price_list_code": "IMPORT",
    "location_code": "IMPORT-WH",
    "location_name": "Import Warehouse",
    "csv": "product_name,sku_code,unit_price,quantity_on_hand\nStarter Pack,STARTER-1,49.00,10"
  }'
{
  "status": "ok",
  "currency": "USD",
  "price_list_code": "IMPORT",
  "location_code": "IMPORT-WH",
  "imported_rows": 1,
  "sample_order_item": {
    "sku_id": "sku_...",
    "quantity": 1
  },
  "created_or_updated": {
    "products": 1,
    "skus": 1,
    "prices": 1,
    "inventory_rows": 1,
    "price_lists": 1,
    "locations": 1
  }
}
The import is idempotent on product/SKU codes. Re-running the same CSV updates the starter catalog instead of creating duplicate rows.
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py catalog-csv-import \
  --csv-file ./catalog.csv \
  --price-list-code IMPORT \
  --location-code IMPORT-WH
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py buyer-link-create \
  --catalog-csv ./catalog.csv \
  --from-csv-row 1 \
  --url-only
ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py buyer-links-batch-from-csv \
  --catalog-csv ./catalog.csv \
  --start-row 1 \
  --end-row 10 \
  --merchant-reference-prefix launch-20260316 \
  --output csv

Order actions

POST /v1/orders/{order_id}/confirm – confirm order

POST /v1/orders/{order_id}/cancel – cancel order

{
  "reason": "customer request",
  "metadata": {"source": "support"}
}

Webhooks (Outbound)

Manage tenant webhook endpoints:

  • GET /v1/webhooks/endpoints
  • POST /v1/webhooks/endpoints
  • DELETE /v1/webhooks/endpoints/{endpoint_id}
  • GET /v1/webhooks/deliveries
  • GET /v1/webhooks/metrics
{
  "url": "https://example.com/webhooks/ordercore",
  "events": ["checkout.completed"]
}
The create response returns a secret once. Store it securely. Supported direct-AI event today: checkout.completed (event names are normalized to lowercase).

DELETE /v1/webhooks/endpoints/{endpoint_id} now deactivates the endpoint instead of erasing it. Delivery history and metrics stay available for ops and support after an endpoint is retired.

Every delivery includes these headers:

  • X-OrderCore-Event – event name such as checkout.completed
  • X-OrderCore-Delivery-ID – unique delivery identifier
  • X-OrderCore-Signaturesha256= + hex HMAC of the raw request body using your endpoint secret

Production delivery is no longer single-shot. Retryable failures stay pending and are retried automatically with capped backoff. Non-retryable 4xx failures move to failed; exhausted retries move to abandoned.

GET /v1/webhooks/deliveries now returns attempt and next_retry_at so support can see whether a delivery is still scheduled for retry.

GET /v1/webhooks/metrics?event_type=checkout.completed&window_hours=24 returns tenant-scoped success rate, pending count, abandoned count, and p95 delivery latency for the selected window.

Verify the signature against the raw, unparsed request body before accepting the event.

// Node.js
import crypto from "node:crypto";

function verifyOrderCoreSignature(rawBody, signatureHeader, endpointSecret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", endpointSecret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader || "", "utf8"),
  );
}
// Go
func verifyOrderCoreSignature(rawBody []byte, signatureHeader, endpointSecret string) bool {
    mac := hmac.New(sha256.New, []byte(endpointSecret))
    mac.Write(rawBody)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signatureHeader))
}

Production smoke coverage now includes a synthetic checkout.completed delivery via ./scripts/smoke_checkout_completed_webhook.sh and a fail-first retry verification via ./scripts/smoke_checkout_completed_retry_worker.sh.

UCP (Experimental)

Discovery: GET /ucp/.well-known/ucp

Checkout sessions:

  • POST /ucp/checkout/sessions
  • PUT /ucp/checkout/sessions/{session_id}
  • POST /ucp/checkout/sessions/{session_id}/complete

Errors

{"error":"unauthorized","message":"Missing API key"}
{"error":"unauthorized","message":"API key expired","auth_reason":"expired_api_key"}
{"error":"rate_limited","message":"Too many requests"}
{"error":"not_implemented","message":"This endpoint is not yet implemented"}

Rate limits

Default: 1000 requests/minute per API key (best-effort).