curl --request GET \
--url https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront \
--header 'Authorization: Bearer <token>' \
--header 'X-Client-Brand-Ids: <x-client-brand-ids>' \
--header 'X-Client-Id: <x-client-id>'import requests
url = "https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront"
headers = {
"X-Client-Id": "<x-client-id>",
"X-Client-Brand-Ids": "<x-client-brand-ids>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'X-Client-Id': '<x-client-id>',
'X-Client-Brand-Ids': '<x-client-brand-ids>',
Authorization: 'Bearer <token>'
}
};
fetch('https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront', 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/account/brands/{brand_id}/storefront",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Client-Brand-Ids: <x-client-brand-ids>",
"X-Client-Id: <x-client-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"
"net/http"
"io"
)
func main() {
url := "https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<x-client-id>")
req.Header.Add("X-Client-Brand-Ids", "<x-client-brand-ids>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront")
.header("X-Client-Id", "<x-client-id>")
.header("X-Client-Brand-Ids", "<x-client-brand-ids>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<x-client-id>'
request["X-Client-Brand-Ids"] = '<x-client-brand-ids>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"brand_id": "<string>",
"offering_ref": "<string>",
"pricing": {
"prices": [
{
"brand_id": "<string>",
"currency": "<string>",
"sku": "<string>",
"version": 2,
"compare_price_minor": 123,
"introductory_price_minor": 123
}
],
"list_price": {
"amount_minor": 123,
"currency": "<string>"
},
"offering": {
"brand_id": "<string>",
"offering_ref": "<string>",
"billing_frequency": "one_time",
"description": "<string>",
"display_name": "<string>",
"includes": [
"<string>"
],
"shipping_frequency": "none",
"version": 2
},
"price_status": "available"
},
"readiness": {
"brand_id": "<string>",
"checks": [
{
"check": "<string>",
"detail": "<string>",
"kind": "<string>",
"blocking": true,
"result": "<string>"
}
],
"offerings": {
"rows": [
{
"checks": [
{
"check": "<string>",
"detail": "<string>",
"kind": "<string>",
"blocking": true,
"result": "<string>"
}
],
"offering_ref": "<string>",
"status": "<string>"
}
],
"source_readable": true
}
}
}
}{
"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>"
}Read one offering's price and whether your brand can sell it
Everything a storefront page needs before it renders a price: the current price and terms for one of your offerings, and your brand’s readiness to sell it, in one request. The two travel together on purpose — a price on its own does not tell you whether a purchase would go through, and a page that shows one without the other can offer a customer something the checkout will refuse. The price is the same price the checkout charges, from the same source; this endpoint never computes a second one. Read price_status before the amount: pending means the terms are not fully set up yet, and no amount is shown rather than a guessed one. Read the readiness block before you render a buy control. You may ask about your own brands only; any other brand is not found. This read changes nothing. CALLING IT FROM A BROWSER: a web page may call this read directly. Present a publishable brand key — sent as Authorization: Bearer pub_live_…, or pub_test_… outside production — which is minted for a single brand, is read-only, and is meant to be shipped in page source. The key reaches only the operations its allowlist names: today that is this read and that brand’s readiness read, and nothing else — every other operation, and every write, is refused. Because it is minted for one brand it may only ask about that brand — any other brand, a sibling brand on your own account included, answers the same not found as a brand that does not exist, and the refusal never tells you which of those it was. A cross-origin page must also have its exact origin registered on that brand’s browser-origin allowlist, so publishing the key does not by itself open the read to any site. The server path is unchanged — call this endpoint from your own server with your secret API key exactly as before, and never put that secret key in page source.
curl --request GET \
--url https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront \
--header 'Authorization: Bearer <token>' \
--header 'X-Client-Brand-Ids: <x-client-brand-ids>' \
--header 'X-Client-Id: <x-client-id>'import requests
url = "https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront"
headers = {
"X-Client-Id": "<x-client-id>",
"X-Client-Brand-Ids": "<x-client-brand-ids>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'X-Client-Id': '<x-client-id>',
'X-Client-Brand-Ids': '<x-client-brand-ids>',
Authorization: 'Bearer <token>'
}
};
fetch('https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront', 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/account/brands/{brand_id}/storefront",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Client-Brand-Ids: <x-client-brand-ids>",
"X-Client-Id: <x-client-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"
"net/http"
"io"
)
func main() {
url := "https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<x-client-id>")
req.Header.Add("X-Client-Brand-Ids", "<x-client-brand-ids>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront")
.header("X-Client-Id", "<x-client-id>")
.header("X-Client-Brand-Ids", "<x-client-brand-ids>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.purplelabelmd.com/v1/account/brands/{brand_id}/storefront")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<x-client-id>'
request["X-Client-Brand-Ids"] = '<x-client-brand-ids>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"brand_id": "<string>",
"offering_ref": "<string>",
"pricing": {
"prices": [
{
"brand_id": "<string>",
"currency": "<string>",
"sku": "<string>",
"version": 2,
"compare_price_minor": 123,
"introductory_price_minor": 123
}
],
"list_price": {
"amount_minor": 123,
"currency": "<string>"
},
"offering": {
"brand_id": "<string>",
"offering_ref": "<string>",
"billing_frequency": "one_time",
"description": "<string>",
"display_name": "<string>",
"includes": [
"<string>"
],
"shipping_frequency": "none",
"version": 2
},
"price_status": "available"
},
"readiness": {
"brand_id": "<string>",
"checks": [
{
"check": "<string>",
"detail": "<string>",
"kind": "<string>",
"blocking": true,
"result": "<string>"
}
],
"offerings": {
"rows": [
{
"checks": [
{
"check": "<string>",
"detail": "<string>",
"kind": "<string>",
"blocking": true,
"result": "<string>"
}
],
"offering_ref": "<string>",
"status": "<string>"
}
],
"source_readable": true
}
}
}
}{
"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>"
}Authorizations
Per-client API key (M2M). Presented as Authorization: Bearer <key>.
Headers
the resolved account, set by the gateway.
1the brands the resolved account owns, set by the gateway.
1which kind of key was presented, set by the gateway.
publishable, secret Path Parameters
the brand to read. Must be one of your own brands; any other brand is not found (404, never 403 — existence is never revealed across accounts).
Query Parameters
the offering to price. Required.
1Response
the offering's price and the brand's readiness to sell it
One offering's price and the brand's readiness to sell it. Read price_status inside pricing before the amount, and read readiness before you render a buy control — a price that resolves on a brand that is not ready to sell is a purchase the checkout will refuse.
Show child attributes
Show child attributes