curl --request POST \
--url https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-Brand-Id: <x-brand-id>' \
--data '
{
"journey_id": "<string>",
"offering_ref": "<string>",
"promo": "<string>"
}
'import requests
url = "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions"
payload = {
"journey_id": "<string>",
"offering_ref": "<string>",
"promo": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-Brand-Id": "<x-brand-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-Brand-Id': '<x-brand-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({journey_id: '<string>', offering_ref: '<string>', promo: '<string>'})
};
fetch('https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'journey_id' => '<string>',
'offering_ref' => '<string>',
'promo' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-Brand-Id: <x-brand-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions"
payload := strings.NewReader("{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-Brand-Id", "<x-brand-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-Brand-Id", "<x-brand-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-Brand-Id"] = '<x-brand-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"amount": {
"amount_minor": 123,
"currency": "<string>"
},
"client_secret": "<string>",
"offering_ref": "<string>",
"order_ref": "<string>",
"payment_state": "created",
"stripe_account": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 503,
"title": "the pricing catalog is temporarily unavailable — checkout cannot resolve the price (fail closed)",
"type": "commerce/checkout/catalog-unavailable"
}Start a checkout session and get a token to collect payment
Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient’s card with Stripe’s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price.
curl --request POST \
--url https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-Brand-Id: <x-brand-id>' \
--data '
{
"journey_id": "<string>",
"offering_ref": "<string>",
"promo": "<string>"
}
'import requests
url = "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions"
payload = {
"journey_id": "<string>",
"offering_ref": "<string>",
"promo": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-Brand-Id": "<x-brand-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-Brand-Id': '<x-brand-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({journey_id: '<string>', offering_ref: '<string>', promo: '<string>'})
};
fetch('https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'journey_id' => '<string>',
'offering_ref' => '<string>',
'promo' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-Brand-Id: <x-brand-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions"
payload := strings.NewReader("{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-Brand-Id", "<x-brand-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-Brand-Id", "<x-brand-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.purplelabelmd.com/v1/payments/checkout-sessions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-Brand-Id"] = '<x-brand-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"journey_id\": \"<string>\",\n \"offering_ref\": \"<string>\",\n \"promo\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"amount": {
"amount_minor": 123,
"currency": "<string>"
},
"client_secret": "<string>",
"offering_ref": "<string>",
"order_ref": "<string>",
"payment_state": "created",
"stripe_account": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 123,
"title": "<string>",
"type": "<string>",
"detail": "<string>"
}{
"status": 503,
"title": "the pricing catalog is temporarily unavailable — checkout cannot resolve the price (fail closed)",
"type": "commerce/checkout/catalog-unavailable"
}Authorizations
Per-client API key (M2M). Presented as Authorization: Bearer <key>.
Headers
mandatory for payments (api-style-guide) — replays return the original result
8The brand this request is scoped to. It is validated against your authenticated account; an unknown or unauthorized brand is rejected. The brand is never taken from the request body.
^brd_[A-Za-z0-9][A-Za-z0-9_-]*$Body
The complete input to start a checkout session. Send the offering reference, the enrollment id, and an optional promo code — nothing else. The server determines the price, the fees, and how the charge is routed; a request that adds a price, fee, or routing field is rejected, and there is deliberately no field for a card number or security code.
the enrollment id this checkout belongs to
^jny_[A-Za-z0-9][A-Za-z0-9_-]*$the offering to purchase, taken from the enrollment link. Each supply length (for example 1, 3, 6, or 12 months) is its own offering with its own reference; selecting a plan means sending a different offering reference, not a quantity or term field.
an optional promo code. Promotions are not yet available, so sending one returns a 422 rather than quietly charging full price.
Response
the session was created; use the returned client token to collect payment
The created checkout session. Everything here is safe to show in the patient's browser. The client token is returned only once, in this response, to collect payment; it is never returned again by the status endpoint.
the amount the patient will be charged, as determined by the server
Show child attributes
Show child attributes
a single-use client token. The browser passes it to the payment provider's components to collect the card and complete payment; the card number never reaches Purple.
^ord_[A-Za-z0-9][A-Za-z0-9_-]*$the payment state at creation; it is always awaiting payment until the patient completes it
created, payment_authorized, paid, payment_failed, partially_refunded, refunded, disputed, cancelled the payment account this session's charge was set up on. The browser must load the payment provider's components in THIS account's context for the client token above to work. PRESENT only when the brand's charges are processed on the client's own payment account; OMITTED entirely (never null, never empty) when they are processed on the Purple platform account, because there is no separate account to point the browser at. Treat the field being absent as the signal to use the ordinary platform setup.
^acct_[A-Za-z0-9][A-Za-z0-9_-]*$