Overview

The Public API is the read half of the {{COMPANY_SHORT_NAME}} REST API. Use it to pull data out of your workspace — orders for an analytics warehouse, vendors for an internal dashboard, clients for a marketing tool, products for a catalog mirror. Keys are prefixed pck_live_* and minted from Settings → API keys.

For pushing data in (creating orders, recording payments), use the Integration API instead — different prefix (pck_int_*), different mint surface (Settings → Connectors), different rate-limit + signature middleware. The split is deliberate: read keys are typically long-lived and shared across BI tools, while write keys are pinned to one external storefront per (workspace, store) pair.

  • Base URL: https://api.peptideclients.com
  • Path prefix: /v1/*
  • Auth: Authorization: Bearer pck_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • OpenAPI spec: live at /v1/openapi.json · static mirror at /api/openapi.yaml

What working correctly looks like

If your key is wired up, you should see all of these. If any one is missing, jump to Troubleshooting.

  • GET /v1/health returns {"ok": true} in under 200 ms.
  • A bearer-authenticated GET /v1/clients returns {"rows": [...], "next_cursor": {...}} within a second.
  • Every successful call advances the key's Last used timestamp in Settings → API keys.
  • List endpoints cap at 100 rows per page even if you pass ?limit=10000.
  • Re-paging with the previous response's next_cursor returns the next slice with no gaps and no duplicates.
  • Calls without the bearer return 401 missing_authorization; revoked keys return 401 invalid_api_key.

Setup — mint a key

  1. Sign in and go to Settings → API keys. (This is the READ-key surface. The write-key surface is Settings → Connectors — see Connectors guide.)
  2. Click New API key.
  3. Pick a scope: read_only (default) or read_write. See Scopes below for what read_write unlocks.
  4. Click Create. We show you the plaintext pck_live_* value once — copy it now. We only store the SHA-256 hash; there is no recovery if you close the modal.
  5. Drop the key into your client (BI tool, script, cURL, Postman) and pass it on every request.
One-time reveal

The plaintext key is shown once. If you lose it, revoke the key and create a new one. The data already pulled stays where it is.

Scopes

  • read_only — every GET on the endpoints below. This is what 95% of integrations need.
  • read_write — the read scope, plus the ability to fire synthetic webhook test events (POST /v1/webhooks/test/{endpoint_id}). It does not grant general write access — that's the Integration API's job. Use read_write when you want a test harness to verify a webhook subscriber from CI without going through the operator UI.

Authentication

Every request needs an Authorization header.

Authorization: Bearer pck_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Missing → 401 missing_authorization. Unknown / revoked / expired → 401 invalid_api_key. The two endpoints that don't need auth:

  • GET /v1/health
  • GET /v1/openapi.json

Endpoints

Orders

GET /v1/orders — paginated list of operator orders across every store in the workspace.

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/orders?limit=50'

GET /v1/orders/{id} — one order by UUID.

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/orders/8c1f3e8c-9b1c-4e36-95ff-1c3a8d7d2f01'

Vendors

GET /v1/vendors — paginated supplier CRM (the suppliers you buy from, not the marketplace storefronts).

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/vendors?limit=50'

GET /v1/vendors/{id} — one vendor by UUID. Note: vendors.vendors rows have no slug; use the UUID.

Clients

GET /v1/clients — paginated CRM clients (your customers).

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/clients?limit=50'

Products

GET /v1/products — paginated published shop products belonging to org-linked sellers. Returns an empty list for orgs that don't have a marketplace storefront yet (no shop.vendor_users mapping).

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/products?limit=50'

GET /v1/products/{id} — one product by UUID.

Webhook test

POST /v1/webhooks/test/{endpoint_id} — fire a synthetic webhook.test event at one of your registered endpoints. Requires read_write scope. Useful for CI smoke tests on a downstream subscriber.

curl -X POST \
  -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/webhooks/test/c0bb1e9f-...'

See the Outbound webhooks guide for the receiver-side signature verification.

Keyset pagination

Every list endpoint uses keyset cursors on (updated_at desc, id desc). The response shape:

{
  "rows": [
    { "id": "8c1f3e8c-...", "updated_at": "2026-05-19T12:00:00Z", ... },
    { "id": "1b1e9d40-...", "updated_at": "2026-05-19T11:58:12Z", ... }
  ],
  "next_cursor": { "at": "2026-05-19T11:58:12Z", "id": "1b1e9d40-..." }
}

To page forward, pass the previous response's cursor as query params:

curl -H 'Authorization: Bearer pck_live_xxxxx' \
  'https://api.peptideclients.com/v1/orders?limit=50&cursor_at=2026-05-19T11:58:12Z&cursor_id=1b1e9d40-...'

The last page returns next_cursor: null.

  • limit — 1 to 100. Default 50. Values above 100 are silently clamped (no error).
  • cursor_at — the updated_at timestamp from the previous page's next_cursor.at.
  • cursor_id — the UUID from the previous page's next_cursor.id.
Why keyset, not offset?

Keyset cursors are stable under writes — a row created mid-iteration can never push a row off the next page or duplicate one onto it. Offset pagination is forbidden by the project's pagination rule; every list query in the system uses this same shape.

Rate limits

The cap is 600 requests / minute per key, enforced best-effort per instance. Over the cap returns:

HTTP/1.1 429
Retry-After: 60
Content-Type: application/json

{ "error": "rate_limited" }

The 600/min cap fits most BI tools (a 50-row page every 5 seconds = 12 req/min for hours of catch-up). If you legitimately need more, contact {{CONTACT_EMAIL}} — sustained >10 req/sec on a read API usually means a different access pattern (server-side cache, mirror table, Supabase logical replication) is the right answer.

OpenAPI spec

Two ways to consume the canonical schema:

  • Live: /v1/openapi.json — always reflects the deployed function. No auth required.
  • Static mirror: /api/openapi.yaml — checked into the docs repo, useful for code generation in CI.

Use either one with the OpenAPI Generator, Postman, Hoppscotch, or the SDK of your choice to scaffold a typed client in your language of choice.

Multi-store note

pck_live_* keys are scoped to the whole workspace (org), not to one store. A single read key returns rows from every store the workspace has. This is the opposite of pck_int_* integration keys, which are pinned to one (workspace, store) pair forever.

The asymmetry is intentional. Reads are usually for “give me everything in this org so my warehouse can join across stores”. Writes are usually for “this WooCommerce site corresponds to one store”. If your read use case needs per-store filtering, the store_id field on each row lets your downstream code do that cheaply.

Settings & permissions

  • Where: Settings → API keys in the operator app (/settings/api-keys).
  • Who can mint keys: org owners and org admins.
  • Who can revoke keys: org owners and org admins.
  • Audit: every create + revoke writes a row to core.audit_log; surfaces in Settings → Audit.
  • Last used: every successful authentication advances the key's last_used_at; check it before revoking to see if the key is still in use.

Troubleshooting

The full symptom-to-fix table lives at Troubleshooting. Quick links:

  • 401 missing_authorization — you forgot the Authorization header. The Bearer prefix is required.
  • 401 invalid_api_key — the key was revoked, expired, or you typo'd a character. Re-check the value against Settings → API keys.
  • 403 insufficient_scope — you hit POST /v1/webhooks/test/{id} with a read_only key. Mint a read_write key for test harnesses.
  • 404 on a UUID that exists — that row belongs to a different workspace, or the path doesn't match the UUID regex. Read the response body's error field.
  • 429 rate_limited — pace your client to under 600 req/min. Honor the Retry-After header.