For the complete documentation index, see llms.txt. This page is also available as Markdown.

Discovery

The Discovery API is the read-only "what does this tenant support, and what can be sold here?" surface. Three endpoints answer three questions:

  • GET /capabilities — which payment methods, currencies, and gateway integrations is the merchant set up for?

  • GET /catalog — which products and payment pages are live, with their lifecycle rules and pricing indicators?

  • GET /manifest — what is the full set of v1 operations available to the calling API key?

These endpoints are designed for client bootstrapping: a storefront, mobile app, or partner portal calls them once on load to render the right buttons, currency pickers, and CTA copy without having to encode tenant-specific assumptions.

All three require the read scope. Responses are deterministic per tenant + API key and are safe to cache for the duration of the session.

The machine-readable specification at /api/v1/openapi.json, and the same document at /.well-known/agent-tools.json, are public and read-only. They need no API key, accept no credentials, and are served with Access-Control-Allow-Origin: *, so browser-based tools and AI agents can fetch them directly.

Endpoints

GET /api/v1/capabilities

Discover merchant capabilities including payment methods and supported currencies

get
/capabilities
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Responses
200

Merchant capabilities

No content

get/capabilities
GET /api/v1/capabilities HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Accept: */*
200

Merchant capabilities

No content

Returns the merchant capability set: enabled paymentMethods (card, ach, sepa, paypal, …), supported currencies, and the active billing-adapter integration metadata (provider name, whether it supports refunds, whether it supports off-session charges). Use it to drive the payment-page UI without hardcoding "we support card and ACH".

supportedCurrencies is now tenant-specific. It previously returned a fixed ["USD"] for every merchant regardless of what they actually transacted in. It now reflects the merchant's own currency registry, resolved in three tiers:

  1. the currencies an administrator has explicitly enabled for the tenant;

  2. failing that, the distinct currencies present in the tenant's synced product catalog;

  3. failing that, ["USD"].

Treat it as the authoritative list for rendering a currency picker. A currency absent from this list is one the merchant is not set up to accept, and a checkout attempted in it should not be offered.

Requires the read scope.

GET /api/v1/catalog

List catalog products enriched with lifecycle rules and pricing indicators, plus payment pages

get
/catalog
Required scopes
This endpoint requires the following scopes:
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Responses
200

Enriched catalog with products and payment pages

application/json
paymentPagesobject[]Optional

Payment page summaries

get/catalog
GET /api/v1/catalog HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Accept: */*
200

Enriched catalog with products and payment pages

{
  "products": [
    {
      "id": "text",
      "name": "text",
      "sku": "text",
      "description": "text",
      "category": "text",
      "productType": "text",
      "priceCents": 1,
      "currency": "text",
      "billingInterval": "text",
      "isActive": true,
      "lifecycleRules": {
        "upgrade": {},
        "downgrade": {},
        "cancellation": {},
        "addOn": {}
      },
      "pricingRules": {
        "hasDynamicPricing": true,
        "pricingTierCount": 1
      }
    }
  ],
  "paymentPages": [
    {}
  ]
}

Returns the live catalog: every active product enriched with the lifecycle rules that govern it (cancellation rule, plan-change rule, applicable add-ons), pricing indicators (hasVariablePricing, hasTieredPricing), and the list of payment pages (CMS slugs) that can sell them.

This is the right call to power a storefront or pricing page; for transactional reads (resolve a single price), use GET /pricing/resolve. For catalog editing the admin surface, use the internal catalog routes, not this endpoint.

Requires the read scope.

GET /api/v1/catalog/products

List catalog products, flagged by whether the API may write them

get
/catalog/products

Each row carries writable: true for native internal/goods products, false for provider-synced rows and bundle parents — so an agent can tell what it may edit without attempting a write.

Required scopes
This endpoint requires the following scopes:
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Query parameters
limitinteger · min: 1 · max: 100Optional

Max items to return (1–100, default 20)

Default: 20
offsetintegerOptional

Number of items to skip (default 0)

Default: 0
productTypestringOptional

Filter by product type (synced, internal, bundle, goods)

Responses
200

Paginated products

No content

get/catalog/products
GET /api/v1/catalog/products HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Accept: */*
200

Paginated products

No content

Every row carries a writable flag: true for native internal and goods products, false for provider-synced rows and bundle parents.

Read it rather than attempting a write and interpreting the 409 — it tells an agent up front which parts of the catalog it may author.

Filter with ?productType= (synced, internal, bundle, goods).

POST /api/v1/catalog/products

Create a native catalog product

post
/catalog/products

productType is REQUIRED and must be internal or goods. Provider-owned (synced) products cannot be created, and provider / externalId / sourceIntegrationId cannot be set — those mark a row as sync-owned, and a later catalog sync could then match and overwrite it.

Required scopes
This endpoint requires the following scopes:
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Body
productTypestring · enumRequiredPossible values:
namestringRequired
skustringOptional
descriptionstringOptional
categorystringOptional
priceCentsintegerOptional
currencystringOptional
imageUrlstringOptional
isActivebooleanOptional
Responses
201

Product created

No content

post/catalog/products
POST /api/v1/catalog/products HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 159

{
  "productType": "internal",
  "name": "text",
  "sku": "text",
  "description": "text",
  "category": "text",
  "priceCents": 1,
  "currency": "text",
  "imageUrl": "text",
  "isActive": true
}

No content

Creates a native product — one authored in PeakCommerce rather than mirrored from a billing provider.

productType is required and must be internal or goods. It has no default here on purpose: the underlying column defaults to synced, so an omitted type would quietly mint a row claiming provider ownership that nothing upstream actually has.

provider, externalId and sourceIntegrationId are rejected. They mark a row as sync-owned, and a later catalog sync matches rows by (tenant, externalId, provider) — a native product carrying them could be matched and overwritten.

bundle products cannot be created here. A bundle is a composite parent whose children live in product_bundle_items; creating the parent alone would yield a product that resolves to nothing.

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

Get one catalog product

get
/catalog/products/{id}
Required scopes
This endpoint requires the following scopes:
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Path parameters
idstringRequired
Responses
200

Product, with a writable flag

No content

get/catalog/products/{id}
GET /api/v1/catalog/products/{id} HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Accept: */*

No content

Notes, examples, or caveats for GET /catalog/products/{id} go here.

PATCH /api/v1/catalog/products/{id}

Update a native catalog product

patch
/catalog/products/{id}

Refused with 409 for provider-synced rows (product_is_provider_owned) and for bundle parents (product_is_bundle). productType cannot be changed.

Required scopes
This endpoint requires the following scopes:
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Path parameters
idstringRequired
Body
namestringOptional
descriptionstringOptional
categorystringOptional
priceCentsintegerOptional
isActivebooleanOptional
Responses
200

Product updated

No content

patch/catalog/products/{id}
PATCH /api/v1/catalog/products/{id} HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 85

{
  "name": "text",
  "description": "text",
  "category": "text",
  "priceCents": 1,
  "isActive": true
}

No content

Only native internal and goods products can be edited.

A provider-synced product is refused with 409 product_is_provider_owned. This is not a permissions quirk — a billing provider owns that row, the catalog sync upserts it by (tenant, externalId, provider), and the nightly drift watcher can trigger a sync without anyone asking. A local edit would look applied, then silently disappear, and misrepresent the provider's catalog until it did. Change it in the provider's catalog and let the sync bring it across.

A bundle is refused with 409 product_is_bundle.

productType cannot be changed — create a new product instead.

GET /api/v1/manifest

Get tenant-level capability manifest describing all available operations

get
/manifest
Authorizations
X-API-KeystringRequired

Scoped secret API key — send an sk_live_… value (keys issued before the typed format carry a bare pk_<hex> prefix and are also secret; they still work). A publishable pk_live_…/pk_test_… key is browser-safe, carries no scopes, and is refused by every endpoint in this spec with publishable_key_not_allowed. Coarse scopes: read, commerce, admin (admin covers everything below). Narrow per-resource scopes: journeys|pages :read|write|publish|delete, productsets :read|write|publish|delete, components|rules :read|write|delete, context :read (read-only context tier), catalog :read|write-native. publish is never implied by write.

Responses
200

Tenant capability manifest

application/json
operationsobject[]Optional
paymentPagesobject[]Optional
capabilitiesobjectOptional
get/manifest
GET /api/v1/manifest HTTP/1.1
Host: api.peakcommerce.app
X-API-Key: YOUR_API_KEY
Accept: */*
200

Tenant capability manifest

{
  "operations": [
    {}
  ],
  "paymentPages": [
    {}
  ],
  "capabilities": {}
}

Returns the full v1 operation manifest as it would apply to the calling API key — operation id, HTTP method + path, the scope it requires, and whether it is currently enabled for this tenant (some operations are gated by the active billing adapter or by a feature flag).

Useful for SDK / CLI clients that need to discover the supported surface dynamically rather than ship a baked-in OpenAPI spec.

Requires the read scope.

Rate limiting (Phase 1a — shadow mode). Every /api/v1/* request is observed by the per-tenant governance middleware (mounted after API-key authentication). Phase 1a is shadow-only: no 429 responses are returned, and rate-limit / concurrency response headers are off by default. Aggregate counts are available to platform admins at GET /api/admin/governance/stats.

Last updated

Was this helpful?