Skip to main content
An offering carries more than one identifier, and each call accepts a specific one. This page is the reference for which id goes where, and for the typed error responses — RFC 7807 application/problem+json — your integration meets along the way.

Identifiers

Every offering has two machine identifiers. The catalog console shows both on an offering’s detail screen, each one copyable and labeled by the call that accepts it — so you never have to guess one or compose it by hand. The code you read on an offering screen (SYN-TRZ-3M) is the offering_ref — there is no separate lookup, and no display-versus-machine split for it in this version. Pass that code wherever an offering_ref is asked for. The sku_id is a different string (sku_…); it is the id the prospect-capture call expects, not the merchandising or checkout call. Of these calls, POST /v1/payments/checkout-sessions and GET /v1/payments/{order_ref}/status appear on the API reference tab. The merchandising read and prospect capture are not in the published reference: prospect capture (POST /v1/prospects) is made by the platform’s own hosted checkout on its server, so your storefront never calls it directly.

The enrollment thread

A patient’s enrollment carries a journey_id (shaped jny_…), minted the first time an intake session resolves. On the hosted platform it travels in the X-Journey-Id request header — that header is how the platform threads one patient’s context across calls. Send it back on a later GET /v1/instrument/resolve to pick an in-progress questionnaire up where the patient left off, and it is the same id you see on webhook events and on the enrollment status read (GET /v1/journeys/{journey_id}/status). The X-Journey-Id header is what the hosted platform reads to resume; a journey_id query parameter is honored only by the standalone renderer used for direct testing, so build against the header.

Problem responses

Every refusal is an application/problem+json body: a stable type, a short title, and the HTTP status repeated in the body. Switch on the type — it is the stable, machine-readable part.

The API-key wall

The platform’s key-protected reads — for example, your account’s orders — sit behind your API key in an Authorization: Bearer header, scoped to a brand you own with X-Brand-Id:
  • A request with no key is refused 401 https://purple.md/problems/unauthenticated.
  • A valid key paired with a brand that is not yours is refused 404 https://purple.md/problems/brand-not-found — never 403, so a brand’s existence never leaks across tenants.

The typed responses you will meet

The checkout create can also return commerce/checkout/offering-not-purchasable (422) — the offering resolved but has no usable price binding. The checkout create requires an Idempotency-Key request header of at least eight characters, and the only body keys it accepts are offering_ref, journey_id, and an optional promo. One more typed response comes from the enrollment status read rather than checkout: That read can also return 503: the status source was unavailable, so the read did not succeed. That tells you nothing about the enrollment itself — retry with backoff, and do not read a failed call as a statement about the patient. Both sit alongside the 400/401/404 refusals above, not in place of them.

When a refusal is not namespaced

Not every refusal carries a namespaced type. Some the platform makes before it reaches the checkout logic — a request whose body is not valid JSON, or one missing its X-Brand-Id — come back with the RFC 7807 default type of about:blank, still carrying a title, the HTTP status, and a detail. So switch on type when it is namespaced, and fall back to the HTTP status when it is about:blank. The list above is the set of named types you will meet; treat it as the ones worth branching on, not as an exhaustive catalog of every possible refusal.

Reading a 404

Not every 404 means the same thing:
  • A 404 whose type is namespaced (commerce/…) means your call reached the platform and the resource is mounted but not configured — fix your configuration or your pricing, not your URL.
  • A 404 with the generic type https://purple.md/problems/not-found means the path itself is not a routable resource — check the URL and the /v1 prefix.
One ordering detail on the key-authenticated calls: a request with no key is refused 401 before the path is routed, so a wrong path sent without a key returns that 401 rather than the generic 404.