PB&J Platform API
Reference for orders, customers, and inventory endpoints. Includes enum values, request schemas, and error responses.
Overview
Every order is tied to a customer_id and returns a unique order_id that you
use for follow-up GET, PUT, and DELETE requests. Orders reserve inventory and release it when
canceled.
- Send
X-API-Keywith every request. - Use
customer_idto track the customer andorder_idto track the order. - Order statuses:
queued,prepping,toasting,ready,delivered,canceled.
Enum values
Bread
Peanut butter
Jelly
Toast and cut options
Create an order for a customer. The API reserves inventory and returns an order_id you
can use to check status.
Query parameters
None.
Body parameters
| Field | Type | Description | Allowed values |
|---|---|---|---|
| customer_id * | string | Customer identifier for order history and reorders. | — |
| bread * | string | Bread choice for the sandwich. | sourdough, brioche, whole_wheat, rye |
| peanut_butter * | string | Peanut butter style for the order. | smooth, chunky, honey_roast |
| jelly * | string | Jelly flavor for the order. | strawberry, grape, raspberry, apricot |
| toast | string | Toast level for the sandwich. | light, golden, dark |
| cut | string | Cut style for the sandwich. | diagonal, halves, triangles |
| notes | string | Free-form instructions for the prep line. | — |
| extras | array | Optional add-ons for the order. | banana_slices, marshmallow_fluff, honey_drizzle |
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| order_id | string | Unique order identifier. | — |
| customer_id | string | Customer identifier for the order. | — |
| status | string | Current order status. |
queued, prepping, toasting, ready,
delivered, canceled
|
| eta_minutes | number | Estimated minutes until ready. | — |
| choices | object | Selections for the order. | — |
| choices.bread | string | Bread selection. | sourdough, brioche, whole_wheat, rye |
| choices.peanut_butter | string | Peanut butter selection. | smooth, chunky, honey_roast |
| choices.jelly | string | Jelly selection. | strawberry, grape, raspberry, apricot |
| choices.toast | string | Toast selection. | light, golden, dark |
| choices.cut | string | Cut selection. | diagonal, halves, triangles |
| choices.extras | array | Optional add-ons for the order. | banana_slices, marshmallow_fluff, honey_drizzle |
Requests
curl -X POST https://api.pbandj.io/v1/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: pbj_test_8dd1f2" \
-d '{
"customer_id": "cus_2048",
"bread": "brioche",
"peanut_butter": "smooth",
"jelly": "raspberry",
"toast": "light",
"cut": "triangles",
"extras": ["banana_slices"]
}'
const response = await fetch('https://api.pbandj.io/v1/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'pbj_test_8dd1f2',
},
body: JSON.stringify({
customer_id: 'cus_2048',
bread: 'brioche',
peanut_butter: 'smooth',
jelly: 'raspberry',
toast: 'light',
cut: 'triangles',
extras: ['banana_slices'],
}),
});
const data = await response.json();
console.log(data);
import requests
response = requests.post(
"https://api.pbandj.io/v1/orders",
headers={"X-API-Key": "pbj_test_8dd1f2"},
json={
"customer_id": "cus_2048",
"bread": "brioche",
"peanut_butter": "smooth",
"jelly": "raspberry",
"toast": "light",
"cut": "triangles",
"extras": ["banana_slices"],
},
)
print(response.json())
Responses
{
"order_id": "ord_41b7",
"customer_id": "cus_2048",
"status": "prepping",
"eta_minutes": 5,
"choices": {
"bread": "brioche",
"peanut_butter": "smooth",
"jelly": "raspberry",
"toast": "light",
"cut": "triangles",
"extras": ["banana_slices"]
}
}
{
"error": "invalid_enum",
"message": "Value 'blueberry' is not a valid jelly option.",
"field": "jelly",
"allowed": ["strawberry", "grape", "raspberry", "apricot"]
}
{
"error": "out_of_stock",
"message": "Strawberry jelly is out of stock.",
"item": {
"type": "jelly",
"option": "strawberry"
},
"suggested": ["grape", "raspberry"]
}
Retrieve an order by order_id.
Path parameters
| Name | Type | Description | Allowed values |
|---|---|---|---|
| order_id * | string | Unique order identifier. | — |
Query parameters
None.
Body parameters
None.
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| order_id | string | Unique order identifier. | — |
| customer_id | string | Customer identifier for the order. | — |
| status | string | Current order status. |
queued, prepping, toasting, ready,
delivered, canceled
|
| eta_minutes | number | Estimated minutes until ready. | — |
| choices | object | Selections for the order. | — |
| choices.bread | string | Bread selection. | sourdough, brioche, whole_wheat, rye |
| choices.peanut_butter | string | Peanut butter selection. | smooth, chunky, honey_roast |
| choices.jelly | string | Jelly selection. | strawberry, grape, raspberry, apricot |
| choices.toast | string | Toast selection. | light, golden, dark |
| choices.cut | string | Cut selection. | diagonal, halves, triangles |
| choices.extras | array | Optional add-ons for the order. | banana_slices, marshmallow_fluff, honey_drizzle |
Requests
Request body: none.
curl https://api.pbandj.io/v1/orders/ord_41b7 \
-H "X-API-Key: pbj_test_8dd1f2"
const response = await fetch('https://api.pbandj.io/v1/orders/ord_41b7', {
headers: {
'X-API-Key': 'pbj_test_8dd1f2',
},
});
const data = await response.json();
console.log(data);
import requests
response = requests.get(
"https://api.pbandj.io/v1/orders/ord_41b7",
headers={"X-API-Key": "pbj_test_8dd1f2"},
)
print(response.json())
Responses
{
"order_id": "ord_41b7",
"customer_id": "cus_2048",
"status": "toasting",
"eta_minutes": 3,
"choices": {
"bread": "brioche",
"peanut_butter": "smooth",
"jelly": "raspberry",
"toast": "light",
"cut": "triangles",
"extras": ["banana_slices"]
}
}
{
"error": "order_not_found",
"message": "No order exists for order_id ord_9001."
}
{
"error": "unauthorized",
"message": "The X-API-Key header is missing or invalid."
}
Update toast, cut, or notes while the order is queued or prepping.
You can update orders only when the status is queued or prepping.
Path parameters
| Name | Type | Description | Allowed values |
|---|---|---|---|
| order_id * | string | Unique order identifier. | — |
Query parameters
None.
Body parameters
| Field | Type | Description | Allowed values |
|---|---|---|---|
| toast | string | New toast level. | light, golden, dark |
| cut | string | New cut style. | diagonal, halves, triangles |
| notes | string | Additional prep instructions for the grill. | — |
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| order_id | string | Unique order identifier. | — |
| status | string | Current order status. |
queued, prepping, toasting, ready,
delivered, canceled
|
| toast | string | Updated toast level. | light, golden, dark |
| cut | string | Updated cut style. | diagonal, halves, triangles |
| notes | string | Updated prep instructions. | — |
| updated_at | string | ISO 8601 timestamp of the update. | — |
Requests
curl -X PUT https://api.pbandj.io/v1/orders/ord_41b7 \
-H "Content-Type: application/json" \
-H "X-API-Key: pbj_test_8dd1f2" \
-d '{
"toast": "dark",
"notes": "Cut corner for easy carry"
}'
const response = await fetch('https://api.pbandj.io/v1/orders/ord_41b7', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'pbj_test_8dd1f2',
},
body: JSON.stringify({
toast: 'dark',
notes: 'Cut corner for easy carry',
}),
});
const data = await response.json();
console.log(data);
import requests
response = requests.put(
"https://api.pbandj.io/v1/orders/ord_41b7",
headers={"X-API-Key": "pbj_test_8dd1f2"},
json={
"toast": "dark",
"notes": "Cut corner for easy carry",
},
)
print(response.json())
Responses
{
"order_id": "ord_41b7",
"status": "prepping",
"toast": "dark",
"cut": "triangles",
"notes": "Cut corner for easy carry",
"updated_at": "2026-03-15T10:21:12Z"
}
{
"error": "invalid_enum",
"message": "Toast must be one of: light, golden, dark.",
"field": "toast"
}
{
"error": "invalid_status",
"message": "Orders can be updated only when the status is queued or prepping.",
"status": "toasting"
}
Cancel an order and release reserved inventory.
You can cancel orders only when the status is queued or prepping.
Path parameters
| Name | Type | Description | Allowed values |
|---|---|---|---|
| order_id * | string | Unique order identifier. | — |
Query parameters
None.
Body parameters
None.
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| order_id | string | Unique order identifier. | — |
| status | string | Order status after cancellation. | canceled |
Requests
Request body: none.
curl -X DELETE https://api.pbandj.io/v1/orders/ord_41b7 \
-H "X-API-Key: pbj_test_8dd1f2"
const response = await fetch('https://api.pbandj.io/v1/orders/ord_41b7', {
method: 'DELETE',
headers: {
'X-API-Key': 'pbj_test_8dd1f2',
},
});
const data = await response.json();
console.log(data);
import requests
response = requests.delete(
"https://api.pbandj.io/v1/orders/ord_41b7",
headers={"X-API-Key": "pbj_test_8dd1f2"},
)
print(response.json())
Responses
{
"order_id": "ord_41b7",
"status": "canceled"
}
{
"error": "order_not_found",
"message": "No order exists for order_id ord_9999."
}
{
"error": "invalid_status",
"message": "Orders can be canceled only when the status is queued or prepping.",
"status": "toasting"
}
Create a customer profile and return a customer_id for future orders.
Query parameters
None.
Body parameters
| Field | Type | Description | Allowed values |
|---|---|---|---|
| name * | string | Customer display name. | — |
| email * | string | Email address for notifications and receipts. | Valid email format |
| favorite_bread | string | Preferred bread choice. | sourdough, brioche, whole_wheat, rye |
| favorite_jelly | string | Preferred jelly flavor. | strawberry, grape, raspberry, apricot |
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| customer_id | string | Unique customer identifier. | — |
| name | string | Customer display name. | — |
| string | Email address on file. | — | |
| favorite_bread | string | Preferred bread choice. | sourdough, brioche, whole_wheat, rye |
| favorite_jelly | string | Preferred jelly flavor. | strawberry, grape, raspberry, apricot |
Requests
curl -X POST https://api.pbandj.io/v1/customers \
-H "Content-Type: application/json" \
-H "X-API-Key: pbj_test_8dd1f2" \
-d '{
"name": "Avery Simmons",
"email": "avery@diner.dev",
"favorite_bread": "sourdough",
"favorite_jelly": "strawberry"
}'
const response = await fetch('https://api.pbandj.io/v1/customers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'pbj_test_8dd1f2',
},
body: JSON.stringify({
name: 'Avery Simmons',
email: 'avery@diner.dev',
favorite_bread: 'sourdough',
favorite_jelly: 'strawberry',
}),
});
const data = await response.json();
console.log(data);
import requests
response = requests.post(
"https://api.pbandj.io/v1/customers",
headers={"X-API-Key": "pbj_test_8dd1f2"},
json={
"name": "Avery Simmons",
"email": "avery@diner.dev",
"favorite_bread": "sourdough",
"favorite_jelly": "strawberry",
},
)
print(response.json())
Responses
{
"customer_id": "cus_2048",
"name": "Avery Simmons",
"email": "avery@diner.dev",
"favorite_bread": "sourdough",
"favorite_jelly": "strawberry"
}
{
"error": "invalid_email",
"message": "Email address must be valid.",
"field": "email"
}
List recent orders for a customer by customer_id.
Path parameters
| Name | Type | Description | Allowed values |
|---|---|---|---|
| customer_id * | string | Customer identifier for the order list. | — |
Query parameters
| Name | Type | Description | Allowed values |
|---|---|---|---|
| status | string | Filter orders by status. |
queued, prepping, toasting, ready,
delivered, canceled
|
| limit | number | Maximum number of orders to return. | 1-100 |
Body parameters
None.
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| customer_id | string | Customer identifier for the list. | — |
| orders | array | Orders for the customer. | — |
| orders[].order_id | string | Order identifier. | — |
| orders[].status | string | Order status. |
queued, prepping, toasting, ready,
delivered, canceled
|
Requests
Request body: none.
curl "https://api.pbandj.io/v1/customers/cus_2048/orders?status=ready&limit=5" \
-H "X-API-Key: pbj_test_8dd1f2"
const response = await fetch(
'https://api.pbandj.io/v1/customers/cus_2048/orders?status=ready&limit=5',
{
headers: {
'X-API-Key': 'pbj_test_8dd1f2',
},
}
);
const data = await response.json();
console.log(data);
import requests
response = requests.get(
"https://api.pbandj.io/v1/customers/cus_2048/orders",
headers={"X-API-Key": "pbj_test_8dd1f2"},
params={"status": "ready", "limit": 5},
)
print(response.json())
Responses
{
"customer_id": "cus_2048",
"orders": [
{ "order_id": "ord_41b7", "status": "ready" },
{ "order_id": "ord_41a2", "status": "delivered" }
]
}
{
"error": "unauthorized",
"message": "The X-API-Key header is missing or invalid."
}
Check current stock for menu options.
Query parameters
None.
Body parameters
None.
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| updated_at | string | ISO 8601 timestamp of the inventory snapshot. | — |
| items | array | Inventory items by category and option. | — |
| items[].type | string | Item category. | bread, peanut_butter, jelly |
| items[].option | string | Menu option name. | — |
| items[].available | number | Units available for new orders. | — |
| items[].reserved | number | Units held by active orders. | — |
| items[].status | string | Stock state. | in_stock, low, out_of_stock |
Requests
Request body: none.
curl https://api.pbandj.io/v1/inventory \
-H "X-API-Key: pbj_test_8dd1f2"
const response = await fetch('https://api.pbandj.io/v1/inventory', {
headers: {
'X-API-Key': 'pbj_test_8dd1f2',
},
});
const data = await response.json();
console.log(data);
import requests
response = requests.get(
"https://api.pbandj.io/v1/inventory",
headers={"X-API-Key": "pbj_test_8dd1f2"},
)
print(response.json())
Responses
{
"updated_at": "2026-03-15T10:12:00Z",
"items": [
{
"type": "bread",
"option": "sourdough",
"available": 14,
"reserved": 2,
"status": "in_stock"
},
{
"type": "jelly",
"option": "strawberry",
"available": 0,
"reserved": 1,
"status": "out_of_stock"
}
]
}
{
"error": "unauthorized",
"message": "The X-API-Key header is missing or invalid."
}
Errors
Response schema
| Field | Type | Description | Allowed values |
|---|---|---|---|
| error | string | Machine-readable error code. |
invalid_enum, invalid_email, unauthorized,
order_not_found, out_of_stock, rate_limited,
invalid_status
|
| message | string | Human-readable error message. | — |
| field | string | Field name that caused the error when applicable. | — |
| allowed | array | Allowed values for the field when applicable. | — |
| item | object | Item details for inventory-related errors. | — |
| status | string | Current order status for invalid status errors. |
queued, prepping, toasting, ready,
delivered, canceled
|
| Status | Code | Message |
|---|---|---|
| 400 | invalid_enum | Selected value is not allowed for this field. |
| 400 | invalid_email | Email address must be valid. |
| 401 | unauthorized | API key is missing or invalid. |
| 404 | order_not_found | No order exists for that order_id. |
| 409 | out_of_stock | Requested item is out of stock. |
| 409 | invalid_status | Order status does not allow this action. |
| 429 | rate_limited | Retry after the reset time. |
{
"error": "out_of_stock",
"message": "Strawberry jelly is out of stock.",
"item": {
"type": "jelly",
"option": "strawberry"
}
}