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/authIdempotency
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):
- Check the API is up:
GET /health— no API key needed - Seed a demo catalog:
POST /v1/onboarding/demo-data— creates demo products, SKUs, inventory, prices and a sample customer - Create your first order:
POST /v1/orders— idempotent, so an agent retry is safe - Confirm the order (optional):
POST /v1/orders/{orderID}/confirm— moves itpending→confirmed
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.
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) check the API is up (no key needed)
curl -sS https://api.ordercore.ai/health
# 2) seed demo catalog / inventory / pricing
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) resolve sample identifiers from the seed response
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 your first order (idempotent — a retry returns the same order)
ORDER_JSON="$(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}]}")"
echo "$ORDER_JSON"
# 5) optional: confirm the order (pending -> confirmed; safe to repeat)
ORDER_ID="$(printf '%s' "$ORDER_JSON" | jq -r '.id // empty')"
curl -sS -X POST "https://api.ordercore.ai/v1/orders/$ORDER_ID/confirm" \
-H "X-API-Key: $API_KEY"jq for onboarding response parsing.Endpoints
The public API surface, grouped by what you are trying to do. Rows marked details have a worked request/response further down this page. The machine-readable contract is /openapi.yaml; this page is the human view of it.
Model packs for OpenAI, Claude, Gemini, DeepSeek and Grok live at /ai-integrations.
Public — no API key
Callable with no credentials. Rate-limited per IP.
/health
Service status. The first call in the first-order flow.
/bootstrap/sandbox-key
Issue a read-only sandbox key for the demo catalog. No signup, no form — the fastest way to a real authenticated response. guide
/direct-ai/manifest
Public Direct-AI checkout manifest: commercial and technical entry points.
/direct-ai/browser-config
Browser-safe checkout config, including the Stripe publishable key when one is configured.
/integrations/catalog
Public add-ons catalog: connectors and the UCP compatibility summary.
/integrations/shopify/status
Public Shopify install diagnostics (non-sensitive).
Orders
The core surface. Order creation is idempotent, so an agent retry is safe.
/v1/orders
Create an order. With Idempotency-Key, replaying the same key returns the original order; the same key with a changed payload returns 409. details
/v1/orders
List orders.
/v1/orders/{order_id}
Order detail.
/v1/orders/lookup
Look up the latest checkout session and order by merchant_reference_id.
/v1/orders/{order_id}/trace
Tenant-safe timeline of order creation, status events and webhook deliveries. No payloads or response bodies. details
Onboarding
Get a catalog to sell against without wiring a connector first.
/v1/onboarding/demo-data
Seed a demo catalog: products, SKUs, inventory, prices and a sample customer. details
/v1/onboarding/catalog-csv
Import a starter catalog, prices and inventory without Shopify or another connector. details
Catalog & inventory
/v1/products
List products. Filters: status.
/v1/products
Create a product.
/v1/products/{product_id}
Product detail.
/v1/products/{product_id}
Update a product.
/v1/products/{product_id}
Delete a product.
/v1/skus/{skuCode}
SKU detail.
/v1/inventory
List inventory. Filters: sku_id/sku_code, location_id.
/v1/inventory/adjust
Adjust stock.
/v1/inventory/reserve
Reserve stock.
/v1/inventory/release
Release reserved stock.
/v1/prices
List prices. Filters: sku_id/sku_code, price_list_code, currency, customer_id, quantity, valid_now, active_only.
Checkout (UCP)
Merchant-authenticated sessions use your API key. The /ucp/public/* mirror is for buyer-facing pages and is authorised by a short-lived checkout token instead.
/ucp/.well-known/ucp
UCP discovery document: version, capabilities and business profile. details
/ucp/checkout/sessions/{session_id}/payment-intent
Create a Stripe PaymentIntent for the session. details
/ucp/checkout/sessions/{session_id}/complete
Complete the session. Idempotent: completing the same session again returns the same order. details
/ucp/public/checkout/sessions/{session_id}/payment-intent
Create a Stripe PaymentIntent for buyer confirmation. details
Account, keys & readiness
/v1/account/auth
Verify the authenticated tenant and key context: auth mode, api_key_scopes (always an array), rate_limit_per_minute, and expiry metadata (api_key_expires_at, api_key_expires_in_days, api_key_expiry_status) when the key has expiry. guide
/v1/account/status
Tenant plan and key status.
/v1/account/readiness
Go-live checklist: key access and TTL stability, catalog/inventory, an active checkout.completed endpoint, successful delivery without recent abandoned failures, and order flow. guide
/v1/account/api-keys
List API keys for the tenant.
/v1/account/api-keys
Create an API key.
/v1/account/api-keys/{api_key_id}/rotate
Rotate a key.
/v1/account/api-keys/{api_key_id}/revoke
Revoke a key.
/v1/account/buyer-checkout-links
Create a branded buyer checkout link scoped to one SKU and quantity, with optional merchant_reference_id (max 128 chars, [a-zA-Z0-9._:-]), success_url and cancel_url.
/v1/account/buyer-checkout-links/regenerate
Mint a fresh branded buyer link from a previous buyer URL or token, keeping the same scope.
Webhooks
/v1/webhooks/endpoints/{endpoint_id}
Deactivate an endpoint. Delivery history and metrics stay queryable after retirement. details
/v1/webhooks/deliveries
Delivery history, including attempt and next_retry_at so you can see whether a delivery is still scheduled for retry. details
/v1/webhooks/metrics
Tenant-scoped success rate, pending count, abandoned count and p95 delivery latency for a window (event_type, window_hours). details
Integrations — Shopify
/v1/integrations/shopify/status
Shopify integration status for the current tenant.
/v1/integrations/shopify/stores
Connected Shopify stores.
/v1/integrations/shopify/sync-logs
Latest Shopify-origin order sync events.
/v1/integrations/shopify/webhook-events
Webhook processing events. Filters: status, shop_domain.
No endpoint matches that filter.
Transaction Trace
Use an order UUID or order number to inspect the lifecycle of one agent purchase. The response combines order creation, status changes, webhook attempts, response status, and delivery duration without returning webhook payloads or response bodies.
curl -sS "https://api.ordercore.ai/v1/orders/OC-2026-0001/trace" \
-H "X-API-Key: oc_live_xxx"{
"trace_version": "2026-07-15",
"order_id": "6c98...",
"order_number": "OC-2026-0001",
"order_status": "confirmed",
"summary": {
"event_count": 3,
"state_changes": 1,
"webhook_deliveries": 1,
"webhook_failures": 0,
"webhook_retries": 0
},
"timeline": [
{"type": "order", "label": "order.created", "status": "pending"},
{"type": "order_event", "label": "order.confirmed", "status": "confirmed"},
{"type": "webhook", "label": "checkout.completed", "status": "success", "attempt": 1}
]
}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:
POST /v1/account/checkout-access-tokensPOST /ucp/public/checkout/sessionsPUT /ucp/public/checkout/sessions/{session_id}POST /ucp/public/checkout/sessions/{session_id}/payment-intent- Confirm
client_secretin Stripe.js / Payment Element POST /ucp/public/checkout/sessions/{session_id}/completewithpayment_data.payment_intent_id
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"
}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 csvpython3 scripts/ordercore_merchant_helper.py help --format jsonpython3 scripts/ordercore_merchant_helper.py help \
--command webhook-metrics \
--format jsonORDERCORE_API_KEY=oc_live_xxx \
node scripts/ordercore_merchant_helper.mjs orders-lookup \
--merchant-reference-id lead-123 \
--allow-missing \
--output jsonlnode scripts/ordercore_merchant_helper.mjs help --format jsonnode scripts/ordercore_merchant_helper.mjs help \
--command webhook-metrics \
--format jsonimport { 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-listORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-endpoints-create \
--url https://example.com/webhooks/ordercore \
--events checkout.completedORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-endpoints-delete \
--endpoint-id 123e4567-e89b-12d3-a456-426614174000Delete 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 10ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-deliveries-list \
--event-type checkout.completed \
--limit 10 \
--output jsonlORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-metrics \
--event-type checkout.completed \
--window-hours 24ORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-metrics \
--event-type checkout.completed \
--window-hours 24 \
--output csvORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-smoke-trigger \
--mode standardORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py webhook-smoke-trigger \
--mode retryableCatalog 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_namesku_code
Optional headers:
product_descriptionproduct_external_idorproduct_refsku_nameunit_priceorpricequantity_on_handorstock_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
}
}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-WHORDERCORE_API_KEY=oc_live_xxx \
python3 scripts/ordercore_merchant_helper.py buyer-link-create \
--catalog-csv ./catalog.csv \
--from-csv-row 1 \
--url-onlyORDERCORE_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 csvOrder 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/endpointsPOST /v1/webhooks/endpointsDELETE /v1/webhooks/endpoints/{endpoint_id}GET /v1/webhooks/deliveriesGET /v1/webhooks/metrics
{
"url": "https://example.com/webhooks/ordercore",
"events": ["checkout.completed"]
}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 ascheckout.completedX-OrderCore-Delivery-ID– unique delivery identifierX-OrderCore-Signature–sha256=+ 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/sessionsPUT /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"}Limits
Three different limits apply. They are not three versions of the same number, and they are measured over different windows on different surfaces. Read the live values from the API rather than hardcoding them.
- Rate limit — 1000 requests per minute, per API key. Best-effort, enforced per key.
GET /v1/account/authreturnsrate_limit_per_minutefor the calling key; read it at startup instead of hardcoding. - Monthly quota — 50,000 API calls per month on the paid plan. Responses carry usage headers, and
GET /v1/account/usagereturns consumption plus a projection for the current period. - Direct-AI evaluation allowance — 10 calls per day. A public allowance on the Direct-AI assistant surface (/ai-integrations) for evaluation, demos and routing checks before billing. It is separate from the two limits above and does not draw on the monthly quota.