<!-- Generated from docs/README.md by scripts/build_public_docs_md.py; do not edit directly. -->
<!-- Machine-readable spec: https://ordercore.ai/openapi.yaml -->
# OrderCore API Guide

This guide documents the public API surface only. Internal business processes
and operational details are intentionally omitted.

## Base URLs

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

## Authentication

Send your API key on every request:

- Header: `X-API-Key: <your_key>`
- Or: `Authorization: Bearer <your_key>`

Example:

```bash
curl -H "X-API-Key: oc_live_..." https://api.ordercore.ai/v1/account/auth
```

## Idempotency

For create operations, include an idempotency key:

- Header: `Idempotency-Key: <key>`
- Or body field: `idempotency_key`
- Max length: `255` characters.
- Reusing the same key with a different request payload returns `409 conflict`.

## Pagination

List endpoints accept:

- `page` (default 1)
- `per_page` (default 20, max 100)

## Five-Minute Quick Start (choose your path)

**A. No signup — see it work offline.** The agent-commerce demo runs a full
shop → checkout → retry flow in mock mode, no key needed:
https://ordercore.ai/agent-commerce-demo (browser) or any runnable example
(Node/Python/LangChain/Vercel AI SDK) from the examples pack.

**B. Wire an AI agent via MCP — one command.** The server is published on npm,
so `npx` fetches it for you. With no key set it issues a read-only sandbox key
automatically — nothing to sign up for:

```bash
claude mcp add ordercore -- npx -y @ordercore/mcp
```

With your own key, the checkout/write tools appear too:

```bash
claude mcp add ordercore --env ORDERCORE_API_KEY=oc_live_xxx -- npx -y @ordercore/mcp
```

Prebuilt binaries for macOS/Linux/Windows: https://ordercore.ai/downloads

Claude Desktop / Cursor config JSON: https://ordercore.ai/what-is-an-mcp-commerce-server

**C. First real order over REST** — the three-call sequence below. Get a key at
https://ordercore.ai/bootstrap: verify your email and your first scoped key is
issued automatically. Or skip signup entirely with an instant read-only sandbox
key (see below). Machine-readable spec for codegen/Actions:
https://ordercore.ai/openapi.yaml

## Instant sandbox key (no signup)

Want a real authenticated call in seconds? Mint a **read-only** sandbox key on the
demo catalog — no signup, no wait:

```bash
KEY=$(curl -sS -X POST https://api.ordercore.ai/bootstrap/sandbox-key | jq -r .api_key)
curl -H "X-API-Key: $KEY" https://api.ordercore.ai/v1/account/auth
curl -H "X-API-Key: $KEY" https://api.ordercore.ai/v1/products
```

The sandbox key is read-only (it cannot create or modify anything), scoped to the
demo tenant, strictly rate limited, and expires within a day. For a full
read/write key, request one below.

## Getting a full API key

Request a key at https://ordercore.ai/bootstrap. We email you a single-use
verification link; confirming it **automatically issues your first scoped
(read+write) key** and emails it to you — no manual step, no sales call.

## Start Here (First Success)

Use this canonical first-success sequence:

1. `GET /health`
2. `POST /v1/onboarding/demo-data`
3. `POST /v1/orders`

```bash
export API_KEY="oc_live_..."

# 1) health preflight
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 onboarding 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 first retry-safe order
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}]}"
```

Notes:
- Requires `jq` for onboarding response parsing.
- Reusing the same idempotency key with a changed payload returns `409 conflict`.

### Fastest first order — with an SDK

No `jq`, a few lines, same guarantee. The `Idempotency-Key` is generated for you,
so a retry returns the same order instead of a duplicate.

```js
// Node — npm install @ordercore/sdk
import { OrderCore } from '@ordercore/sdk';
const oc = new OrderCore({ apiKey: process.env.API_KEY });
const seed = await oc.onboarding.demoData();
const order = await oc.orders.create({
  customer_id: seed.sample_customer_id,
  items: [{ sku_id: seed.sample_order_item.sku_id, quantity: 1 }],
});
console.log(order.id, order.order_number);
```

```python
# Python — pip install ordercore
from ordercore import OrderCore
oc = OrderCore(api_key=API_KEY)
seed = oc.onboarding.demo_data()
order = oc.orders.create({
    "customer_id": seed["sample_customer_id"],
    "items": [{"sku_id": seed["sample_order_item"]["sku_id"], "quantity": 1}],
})
print(order["id"], order["order_number"])
```

Go and more: see the [SDKs](https://github.com/RulezZzOr/ordercore-examples/tree/main/sdk).

## Endpoints

### GET /health
Returns basic service status.

### GET /v1/products
List products.

Query params:
- `status`: `draft | active | archived`
- `page`, `per_page`

### POST /v1/products
Create a product.

### GET /v1/products/{product_id}
Get product detail.

### PATCH /v1/products/{product_id}
Update a product (partial).

### DELETE /v1/products/{product_id}
Delete a product.

### GET /v1/skus/{skuCode}
Get SKU detail by code.

### GET /v1/inventory
List inventory balances.

Query params:
- `sku_id` or `sku_code` (use only one)
- `location_id`
- `page`, `per_page`

### POST /v1/inventory/adjust
Adjust stock levels.

Request body:
```json
{
  "sku_code": "RUNSHOE-43",
  "location_code": "EU-WH-01",
  "quantity_delta": 5,
  "reason": "manual adjustment"
}
```

### POST /v1/inventory/reserve
Reserve stock.

Request body:
```json
{
  "sku_code": "RUNSHOE-43",
  "location_code": "EU-WH-01",
  "quantity": 2,
  "reference_type": "order",
  "reference_id": "uuid"
}
```

### POST /v1/inventory/release
Release reserved stock.

Request body:
```json
{
  "sku_code": "RUNSHOE-43",
  "location_code": "EU-WH-01",
  "quantity": 2,
  "reference_type": "order",
  "reference_id": "uuid"
}
```

### GET /v1/prices
List prices.

Query params:
- `sku_id` or `sku_code` (use only one)
- `price_list_code`
- `currency`
- `customer_id` (optional; resolves active price list, cannot combine with `price_list_code` or `currency`)
- `quantity` (optional; filters by min/max quantity)
- `valid_now` (optional; `true` to filter by validity window)
- `active_only` (optional; `true` to filter active price lists)
- `page`, `per_page`

### POST /v1/orders
Create an order.

Request body:

```json
{
  "customer_id": "cust_123",
  "items": [
    {"sku_id": "sku_123", "quantity": 2}
  ],
  "idempotency_key": "your-idempotency-key"
}
```

Notes:
- `customer_id` may be an internal UUID or an external customer ID.
- `items[].sku_id` is required.
- Validation guardrails:
  - `items` must contain `1..100` entries.
  - `items[].quantity` must be `1..1000`.
  - `items[].sku_id` must be unique within a request (merge quantity when ordering the same SKU).
  - `customer_id`, `external_id`, and `items[].sku_id` are limited to `255` characters.
  - `metadata` JSON payload must not exceed `32768` bytes.

Response (201):

```json
{
  "id": "order_...",
  "status": "pending",
  "created_at": "2026-02-09T12:34:56Z",
  "order_number": "OC-2026-00123"
}
```

### POST /v1/onboarding/demo-data
Create demo catalog data for your tenant (idempotent).

This endpoint is useful right after signup so your workspace is not empty.

Request body (optional):

```json
{
  "currency": "USD",
  "price_list_code": "DEFAULT",
  "location_code": "MAIN-WH"
}
```

### GET /v1/account/auth
Verify authenticated tenant + key context for the supplied API key.

Useful as the first authenticated preflight before onboarding/orders.
Returns tenant + key identity, auth mode, effective key scopes, write-scope readiness, per-key rate limit, and key expiry metadata/warnings when applicable.

Example response:

```json
{
  "tenant_id": "tenant_...",
  "tenant_slug": "demo",
  "plan": "pilot",
  "api_key_id": "key_...",
  "auth_mode": "x-api-key",
  "api_key_scopes": ["read", "write"],
  "has_write_scope": true,
  "rate_limit_per_minute": 240,
  "api_key_expires_at": "2026-04-27T12:00:00Z",
  "api_key_expires_in_days": 31,
  "api_key_expiry_status": "ok",
  "auth_warnings": [],
  "authenticated_at": "2026-03-27T12:00:00Z"
}
```

`auth_warnings` contains machine-readable warning codes for client UX/runtime checks:
- `missing_write_scope`
- `api_key_expires_within_7d`
- `api_key_expires_within_72h`

### GET /v1/account/readiness
Go-live readiness checklist for the authenticated tenant.

This covers active key access (`has_active_api_keys`, `has_write_scope`, stable key TTL), catalog/inventory setup, active `checkout.completed` webhook endpoint setup, recent successful delivery, no abandoned checkout deliveries in the last 24h, and recent order activity.

`has_write_scope` follows the same runtime authz policy as API writes:
- Empty scopes are blocked by default in production.
- Temporary compatibility can be enabled with `ALLOW_EMPTY_API_KEY_SCOPES=true`.

### GET /v1/account/billing
Authenticated tenant billing status snapshot for self-serve management UX.

Returns:
- Stripe customer linkage (`stripe_customer_id` when present)
- Access state (`stripe_access_active`, optional `stripe_access_expires_at`)
- `status`: `active | inactive | expired`
- `billing_portal_url` for self-serve subscription management/cancellation
- machine-readable warnings (for example `missing_stripe_customer_id`, `stripe_access_expires_within_72h`)

### GET /v1/orders
List orders.

Query params:
- `status` (optional)
- `customer_id` (optional; UUID or external_id)
- `page`, `per_page`

### GET /v1/orders/{order_id}
Order detail (includes items).

### GET /v1/orders/{order_id}/trace
Tenant-safe transaction timeline for one order UUID or order number. Combines order creation, status changes, and webhook delivery attempts without returning webhook payloads or response bodies.

The same read-only capability is exposed to MCP clients as `get_order_trace`.

### POST /v1/orders/{order_id}/confirm
Confirm a pending order.

### POST /v1/orders/{order_id}/cancel
Cancel a pending/confirmed/processing order.

### Webhook endpoints
Manage outbound webhooks for your tenant.

- `GET /v1/webhooks/endpoints`
- `POST /v1/webhooks/endpoints`
- `DELETE /v1/webhooks/endpoints/{endpoint_id}`

Create example:

```json
{
  "url": "https://example.com/webhooks/ordercore",
  "events": ["checkout.completed"]
}
```

The response returns a `secret` once; store it securely. Supported direct-AI outbound event today: `checkout.completed` (event names are normalized to lowercase).
Endpoint URL rules: absolute `http(s)` URL, no URL credentials, no query params, no fragments.

### UCP (Beta)

UCP endpoints are available under `/ucp` and require API key auth:

- `GET /ucp/.well-known/ucp`
- `POST /ucp/checkout/sessions`
- `PUT /ucp/checkout/sessions/{session_id}`
- `POST /ucp/checkout/sessions/{session_id}/complete`

Current behavior:
- Session pricing and availability are read from your DB catalog/inventory.
- `complete` now creates a real OrderCore order using the same service logic as `POST /v1/orders`.
- Completion is idempotent per session (`ucp-complete:<session_id>` backend key).
- Returned order status follows your current order pipeline (typically `pending` right after create).

Known beta limits:
- UCP `complete` accepts either a `payment_data.payment_intent_id` (preferred, after client-side Stripe confirmation) or a legacy `payment_data.credential.token` (`pm_...`).
- If Stripe is not configured, `complete` returns `402 payment_failed`.
- Use your Stripe webhook flow for subscription lifecycle and account provisioning.

Minimal UCP smoke (create + complete):

```bash
BASE_URL=https://demo-api.ordercore.ai
API_KEY=oc_live_...

SESSION_ID=$(
  curl -sS -X POST "$BASE_URL/ucp/checkout/sessions" \
    -H "X-API-Key: $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"line_items":[{"item":{"id":"DEMO-BOTTLE-750"},"quantity":1}]}' | \
  jq -r '.id | split("/Checkout/")[1]'
)

curl -sS -X POST "$BASE_URL/ucp/checkout/sessions/$SESSION_ID/complete" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payment_data":{"handler_id":"gpay","type":"google_pay","brand":"visa","last_digits":"4242","credential":{"type":"stripe_payment_method","token":"pm_..."}}}'
```

See `docs/UCP_PAYMENT_TOKENS.md` for details.

### Direct AI Tooling

Machine-readable tool schemas for GPT/Gemini/Claude (Anthropic)/DeepSeek/Grok integrations:

- `GET /direct-ai/manifest`
- `GET /direct-ai/tooling`
- `https://ordercore.ai/ai-integrations`
- `https://ordercore.ai/claude-quickstart`

The tooling endpoint includes canonical tool names, path templates, and request schemas
for this public checkout flow:

1. `create_checkout_session`
2. `update_checkout_session`
3. `create_payment_intent`
4. `complete_checkout_session`

Quick verification smoke:

```bash
BASE_URL=https://api.ordercore.ai make smoke-direct-ai-tooling
```

Export provider-ready payload artifacts (OpenAI/Claude/Anthropic/DeepSeek/Grok/Gemini):

```bash
BASE_URL=https://api.ordercore.ai make export-direct-ai-tooling OUTPUT_DIR=./tmp/direct-ai-tooling
```

Export writes:
- `openai.responses_api.tools.json`
- `deepseek.chat_completions.tools.json`
- `grok.responses_api.tools.json`
- `claude.tools.input_schema.json`
- `anthropic.tools.input_schema.json`
- `gemini.function_declarations.json`

Generate a ready-to-run OpenAI API boarding pack (request template + run helper), no ChatGPT GPT Builder required:

```bash
BASE_URL=https://api.ordercore.ai make openai-api-boarding-pack
```

Generate a combined OpenAI + DeepSeek + Grok + Anthropic API boarding pack:

```bash
BASE_URL=https://api.ordercore.ai make ai-api-boarding-pack
```

If your environment exposes tooling under authenticated `/v1` routes, include an API key:

```bash
ORDERCORE_API_KEY=oc_live_xxx BASE_URL=https://api.ordercore.ai make ai-api-boarding-pack
```

Run a real Anthropic tool-call smoke against OrderCore:

```bash
API_KEY=oc_live_xxx \
ANTHROPIC_API_KEY=sk-ant-... \
BASE_URL=https://api.ordercore.ai \
make e2e-anthropic
```

Install the standalone MCP server from npm (zero config, no clone, no build):

```bash
npx -y @ordercore/mcp
```

Registered in the official MCP Registry as `ai.ordercore/ordercore-mcp`.
Prebuilt binaries for macOS/Linux/Windows are also at
https://ordercore.ai/downloads.

Register with Claude Code in one command:

```bash
claude mcp add ordercore --env ORDERCORE_API_KEY=oc_live_xxx -- npx -y @ordercore/mcp
```

Omit `--env ORDERCORE_API_KEY=...` to run in read-only sandbox mode against the
demo catalog; the server issues a temporary sandbox key itself.

The MCP wrapper exposes two tool groups:

- Checkout tools backed by short-lived checkout tokens: `create_checkout_session`, `update_checkout_session`, `create_payment_intent`, `complete_checkout_session`.
- Catalog/read tools: `search_products`, `get_product`, `get_prices`, `get_inventory`, `get_order_history`, `get_order_trace`.
Claude Desktop config shape:

```json
{
  "mcpServers": {
    "ordercore": {
      "command": "npx",
      "args": ["-y", "@ordercore/mcp"],
      "env": {
        "ORDERCORE_BASE_URL": "https://api.ordercore.ai",
        "ORDERCORE_API_KEY": "oc_live_..."
      }
    }
  }
}
```

### Public integration diagnostics

- `GET /integrations/catalog`
  - Public read-only catalog of available and planned add-ons.
  - Includes UCP compatibility summary.

- `GET /integrations/shopify/status`
  - Public non-sensitive Shopify install diagnostics for app-review checks.
  - Reports whether required env config is present.

### Download package integrity

Validate Woo/Shopify gateway package checksums and page references:

```bash
./scripts/validate_downloads.sh
```

## Errors

Errors are returned as JSON. Examples:

```json
{"error":"unauthorized","message":"Missing API key"}
```

```json
{"error":"unauthorized","message":"API key expired","auth_reason":"expired_api_key"}
```

```json
{"error":"forbidden","message":"API key is missing scopes; rotate key with read/write scopes","auth_reason":"missing_api_key_scopes"}
```

```json
{"error":"forbidden","message":"Insufficient API key scope","auth_reason":"insufficient_api_key_scope"}
```

```json
{"error":"checkout_token_expired","message":"Checkout access token expired","auth_reason":"expired_checkout_access_token"}
```

```json
{"error":"rate_limited","message":"Too many requests"}
```

Roadmap:
- `docs/ADDONS_ROADMAP.md`

```json
{"error":"not_implemented","message":"This endpoint is not yet implemented"}
```

## Rate limits

Default limit is 1000 requests per minute per API key (best-effort).

## Smoke test

Run an end-to-end API smoke test (health, onboarding, list endpoints, idempotent order):

```bash
API_KEY=oc_live_... BASE_URL=https://demo-api.ordercore.ai ./scripts/smoke_api.sh
# Optional: AUTH_MODE=bearer
# Optional: ONBOARDING_REQUEST_MAX_RETRIES=0 (disable retries for onboarding seed write)
```

You can also run:

```bash
API_KEY=oc_live_... make smoke-api
```

## Load test (read-only)

Run an authenticated read-only capacity profile (health/auth/products/prices):

```bash
API_KEY=oc_live_... BASE_URL=https://api.ordercore.ai ./scripts/load_readonly.sh
# Optional: AUTH_MODE=bearer
```

Or:

```bash
API_KEY=oc_live_... make load-readonly
```

`load_readonly.sh` writes raw k6 summary JSON and prints a parsed capacity report.

Run live Cloud Run + Cloud SQL scaling posture audit:

```bash
python3 ./scripts/scaling_posture.py --project ordercore-prod --region europe-west1 --service ordercore-api
# Optional: --db-max-connections 200
```

GitHub Actions `Scaling Posture Audit` supports weekly scheduled runs and optional gate enforcement.

## First order flow

Run a focused onboarding + first order scenario:

```bash
API_KEY=oc_live_... BASE_URL=https://api.ordercore.ai ./scripts/first_order_flow.sh
# Optional: AUTH_MODE=bearer
# Optional: ONBOARDING_REQUEST_MAX_RETRIES=0 (disable retries for onboarding seed write)
```

Or:

```bash
API_KEY=oc_live_... make first-order
```

## E2E integration proof

Run full-depth order proofs for OpenAI + Claude variants (idempotent replay + payload drift `409`):

```bash
API_KEY=oc_live_... make e2e-openai
API_KEY=oc_live_... make e2e-claude
API_KEY=oc_live_... make e2e-suite
```

Detailed runbook:

- `docs/E2E_QUICKSTART.md`

## Pilot onboarding pack

Build Woo gateway package and stage `latest` artifacts for frontend downloads:

```bash
make pilot-pack
```

Full pilot handoff checklist:

- `docs/PILOT_ONBOARDING_PACK.md`
- `docs/GATEWAY_CLIENT_PACK.md`
- `docs/V0.2.15_GATEWAY_SCOPE.md`
- `docs/GATEWAY_TENANT_RUNBOOK.md`
- `integrations/shopify/ordercore-shopify-gateway/CLIENT_QUICKSTART.md`

Run end-to-end gateway handoff smoke:

```bash
API_KEY=oc_live_... make gateway-handoff
```

## Shopify (App Store)

Shopify App Store path (real Shopify OAuth + mandatory webhooks) is documented here:

- `docs/SHOPIFY_APP_STORE.md`

Install flow (tenant-scoped):

```bash
API_KEY=oc_live_...
SHOP=example.myshopify.com
curl -i -H "X-API-Key: $API_KEY" "https://api.ordercore.ai/v1/integrations/shopify/install?shop=$SHOP"
```

## UCP payment tokens

See `docs/UCP_PAYMENT_TOKENS.md` for how to generate and pass Stripe PaymentMethod tokens
into the UCP `complete` call.

## UCP quickstart

See `docs/UCP_QUICKSTART.md` for a minimal end-to-end UCP flow.

UCP demo script:

```bash
API_KEY=oc_live_... SKU_CODE=DEMO-RUNNER-43 ./scripts/ucp_demo_flow.sh
```

## UCP error catalog

See `docs/UCP_ERROR_CATALOG.md` for common error codes and fixes.

## Ops hardening

See `docs/OPS_HARDENING.md` for production monitoring + alerting checklist.

## Support

Contact: support@cloudpeakify.com
Company surface: https://ordercore.ai/company.html
