Orders can be placed as soon as the quick online registration is complete.
Programmatic access to artwork, pricing, shipping, orders, invoices and payment methods.
Base URL https://api.heatapplied.com/v1 ·
OpenAPI document (JSON) ·
Manage API keys & webhooks ·
The API is REST over HTTPS with JSON request and response bodies. All resources live under https://api.heatapplied.com/v1. Object identifiers are UUIDs (orders, invoices, artwork, payment methods) or human numbers where the object has no UUID (shipments use P######).
Recommended order flow: upload artwork → quote pricing → quote shipping → create the order → receive webhooks as it moves through production and ships. Pricing is identical across the quote, rate and order calls when the same inputs are sent.
Create a key under My Account → Settings → API Keys. The secret (sk_live_… / sk_test_…) is shown once. Send it as a Bearer token on every request:
curl "https://api.heatapplied.com/v1/orders" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/orders", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/orders",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Examples throughout this reference default to curl; pick another language from the tabs (or the selector at the top) and every example follows.
Keys carry a scope (* or a list of METHOD:/v1/path entries), optional CIDR allow-list, and a rate limit (default 100 requests per minute). HTTPS is mandatory. Keys act on behalf of the account owner: every resource below names the account permission (My Account → Settings → Users → Manage Access) the owner must hold, and a key whose account lacks it receives 403 security_error even when the key's scope allows the path.
A key owned by a staff account may act for any customer account by adding customer=<customer user uuid or sign-in email> — as a query parameter on GET / DELETE requests or a body field on POST / PATCH. Every Artwork, Order, Invoice, Shipment, Shipping and Pricing resource then reads and writes that customer's account exactly as the customer's own key would: lists scope to the customer, objects are found on the customer, and new artwork or orders are created on the customer. A sub-user reference resolves to its account root. The staff key must hold the admin-console permission named on the resource (for example _AdminConsole_Order_List), the target must be a customer account (404 otherwise), and a customer key that sends customer receives 403. Tickets do not use this parameter — there customer is the staff field naming the ticket's affected account — and API key provisioning always acts on the key's own account.
Non-2xx responses carry {"errors": [{"message": "…", "type": "…"}]}. Validation failures name the offending field in message (e.g. line_items[0].quantity must be a positive integer).
| type | Meaning |
|---|---|
auth_error | Missing, unknown or expired API key. |
security_error | HTTPS required, IP outside approved CIDR blocks, or request outside the key scope. |
invalid_request_error | Missing/invalid parameter or resource not found. |
rate_limit_error | Too many requests — honour the Retry-After header. |
api_connection_error | Upstream connectivity failure. |
api_error | Unexpected server-side error. |
HTTP status codes: 400 validation, 401 bad key, 402 payment declined, 403 scope / CIDR / HTTPS / missing account permission, 404 not found, 429 rate limited (honour Retry-After), 5xx server error.
Every monetary field is an integer in minor units of the object's currency — for USD that is cents: 1234 means $12.34, 50 means $0.50. Amounts are never floats or decimal strings, in requests or responses. Objects that carry money (quote, shipping rate, order, invoice and their line items / transactions) include currency so the unit is unambiguous. Any money value you send must be a whole number of cents; a decimal is rejected with 400 validation.
List endpoints accept page (1-based) and per_page (max 100) and return {"data": […], "page": n, "pages": n, "total": n}. Sort with sort and order where documented. Prefer webhooks to polling for state changes.
Artwork — upload, list, retrieve, update, archive and download the print files you order transfers from.
List variable mutators (staff)
Staff keys. Enumerates the registered variable mutators a master's mutator_rules may use, with each one's params schema. Mutators run in rule order on every derivative data row: splitters write <variable_in>_Line1 / _Line2, cutters rewrite <variable_in>, TransformText rewrites <variable_in> per mode, CreateField writes <variable_in> from a {Var} template, DismissField drops <variable_in> from the output.
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
Paginated list of mutator objects in data.
403 | Key is not owned by a staff account. |
curl "https://api.heatapplied.com/v1/artwork/mutators" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/mutators", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/mutators",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/mutators");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Unarchive artwork
Restores an archived artwork to the active list.
Required account permission: _Account_Artwork_Archive or _AdminConsole_Artwork_Archive
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
artwork object.
404 | Artwork not found on this account. |
409 | Not archived. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Archive artwork
Hides the artwork from listings and blocks new orders for it. Emits the artwork.archived webhook.
Required account permission: _Account_Artwork_Archive or _AdminConsole_Artwork_Archive
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
artwork object.
404 | Artwork not found on this account. |
409 | Already archived. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Download current file
Streams the artwork's current file as an attachment: the file you uploaded until preflight completes, then the print-ready file produced by preflight (each preflight completion creates a new version). System-generated artwork (designer output, pre-spaced names) is locked until it has been ordered at least once.
Required account permission: _Account_Artwork_Download or _AdminConsole_Artwork_Download
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Binary file body with Content-Disposition: attachment.
403 | Download locked until first order. |
404 | Artwork or file not found. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Get master design document (staff)
Staff keys. Returns the schema-v1 design document a lab master was compiled from — the input to re-send (edited) as PATCH … {document}. 409 for a non-master, or for an uploaded Illustrator-package master (no document — download its PDF/package with GET …/download and replace it with POST …/package).
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
uuidrequired | string |
{artwork_uuid, design_uuid, document: {…}}
403 | Key is not owned by a staff account. |
404 | Artwork not found. |
409 | Not a master, or the master has no design document. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/document" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/document", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/document",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/document");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Attach production file (staff)
Staff keys. Attaches a print-ready production PDF to the artwork — the file production actually images from, as Admin → Artwork → Print Files does. The artwork may belong to any customer account. The PDF is stored as a new print_file row for the given print_method_id, versioned above the artwork's existing files for that method (production always picks the highest version); a 72 dpi preview is rendered and the trimmed image / page dimensions are measured from it. The artwork's status is NOT changed — pair with POST /v1/artwork using customer + status: 3, or set the status separately. Send as multipart/form-data.
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
filerequired | file | The production PDF (≤ 50 MB). Only .pdf is accepted. |
print_method_id | integer | Print method the file is prepared for. 1 = Screen Printing Sheet-Fed (default). Default 1 |
name | string | Display name for the file (≤ 255). Defaults to the artwork name. |
print_file object.
400 | Validation failed, file not a PDF, or the PDF could not be rendered. |
403 | Key is not owned by a staff account. |
404 | Artwork or print method not found. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/production" \
-H "Authorization: Bearer sk_live_…" \
-F "file=@team-logo_production.pdf" \
-F "print_method_id=1"import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("team-logo_production.pdf")]), "team-logo_production.pdf");
form.append("print_method_id", "1");
const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/production", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" },
body: form
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/production",
headers={"Authorization": "Bearer sk_live_…"},
files={"file": open("team-logo_production.pdf", "rb")},
data={
"print_method_id": 1,
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/production");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('team-logo_production.pdf'),
'print_method_id' => 1,
],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Ingest a finished derivative (staff)
Staff keys. Adds an already-finished derivative to a master **without running the generator**. :uuid is the MASTER (its uuid, any version of the lineage, or its numeric artwork id); the body carries the completed art file and the variable values that file represents.
Use it when a derivative cannot come from the template: it was drawn and sold before the master existed, or the generated output had to be hand-corrected to look right. The result is a true derivative — it lists under the master, inherits its type, formula, artboard size and colour count, gets the master's press rotations, print colours and tags cloned onto it, and carries one variable row per value you send — so nothing downstream can tell it from a generated one.
variables is a JSON object of variable name to the value the file shows, e.g. {"City":"Dayton","State":"OH"}. Send the values as they appear in the art. If you are sending a raw data row instead and want the master's mutator waterfall applied first, add apply_mutators=1 and the mutated result is what gets stored. name defaults to the master name with the variable values appended (City first, State abbreviated), matching generated derivatives.
status defaults to 3 (Preflight Approved), since the file is finished art; no preflight is run and no preflight webhook fires. Attach the print-ready production PDF afterwards with POST /v1/artwork/:uuid/production using the uuid THIS call returns. Send as multipart/form-data.
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
filerequired | file | The finished derivative art file (≤ 50 MB). Only .pdf is accepted. |
variablesrequired | string | JSON object of variable name to the value the file represents, e.g. {"City":"Dayton","State":"OH"}. Stored as the derivative's resolved values. |
apply_mutators | boolean | When true, run the master's mutator rules over variables first and store the result. Use when sending a raw data row rather than resolved values.Default |
name | string | Derivative name (≤ 255). Defaults to the master name with the variable values appended. |
status | integer | Status to create the derivative in. Defaults to 3 (Preflight Approved) because the supplied file is finished art. One of: 1, 2, 3, 4, 5, 6 · Default 3 |
artwork object.
400 | Validation failed, variables missing or malformed, the target is not a master, or the PDF could not be rendered. |
403 | Key is not owned by a staff account. |
404 | Master artwork not found. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/derivative" \
-H "Authorization: Bearer sk_live_…" \
-F "file=@dayton-oh-finished.pdf" \
-F "variables={\"City\":\"Dayton\",\"State\":\"OH\"}"import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("dayton-oh-finished.pdf")]), "dayton-oh-finished.pdf");
form.append("variables", "{\"City\":\"Dayton\",\"State\":\"OH\"}");
const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/derivative", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" },
body: form
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/derivative",
headers={"Authorization": "Bearer sk_live_…"},
files={"file": open("dayton-oh-finished.pdf", "rb")},
data={
"variables": "{\"City\":\"Dayton\",\"State\":\"OH\"}",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/derivative");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('dayton-oh-finished.pdf'),
'variables' => '{"City":"Dayton","State":"OH"}',
],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Replace master package (staff)
Staff keys. Replaces a master template's finished Illustrator package — the master PDF you hand-finished outside the system — as a NEW version of the master, **without recompiling anything**. :uuid is any version of the master lineage (or its numeric id). The ZIP is File → Package output: the master PDF beside Fonts/ and Links/ (a wrapper folder is fine). The PDF is stored byte for byte, so a custom distress mask, a re-fit envelope mesh or corrected kerning ship exactly as you drew them.
What happens: the PDF is preflighted and refused for the same reasons as creation (several pages, RGB, no Illustrator variables, an ink the colour catalogue cannot name — errors[0].message lists them). A PDF edited outside Illustrator (the master downloaded, its text replaced by a tool, re-uploaded) has lost its variables: send variables as a JSON object of the text as drawn → variable name ({"MISSION BEACH": "City", "CALIFORNIA": "State"}) and the API binds those text objects as Illustrator variables before vetting, exactly as on create (see POST /v1/artwork, "Binding text as variables"); the refusal for a PDF without variables quotes the text it contains so the map can be written from it. Then a new artwork version is opened on the lineage (new uuid, version N+1, the old version archived and kept for history); the new version takes the package's PDF, ZIP, preview, dimensions, colour type and colour count; print colours and press rotations, variables, fonts and linked files are authored fresh from the package; mutator rules are carried from the previous version pruned to the variables that still exist (send mutator_rules to replace them, [] to clear); derivatives are re-pointed and live carts / orders / work orders relinked, as a PATCH {document} recompile does. Works on a lab master too — its Design Lab entry follows the new version, but the package is now what production reads until someone recompiles it with PATCH {document}.
The response is the new version (its uuid differs from the one you sent; master.document_url / master.design_uuid tell you whether a design document still exists). 409 when the artwork is not a master, or when the uuid you sent is no longer the head of its lineage (someone replaced or recompiled it first — fetch again and resend).
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
filerequired | file | The Illustrator Package .zip (≤ 200 MB): exactly one master PDF outside Fonts/ / Links/. |
formula | string | Ink formula for the new version. Defaults to the master's current formula. One of: Formula-1, Formula-2, Formula-4, Formula-5 |
variables | string | JSON object of text as drawn → variable name, e.g. {"MISSION BEACH": "City", "CALIFORNIA": "State"}. Binds those text objects as Illustrator variables in the PDF before it is vetted (default value = the text). Omit when the PDF already carries its variables. Names ≤ 64 characters, no braces / whitespace, each used once. |
mutator_rules | string | JSON array [{"mutator": "<name>", "variable_in": "<key>", "param": {…}}] validated against the variables detected in the new PDF (same rules as create). Omit to carry the current rules, minus any whose variable_in no longer exists; [] clears them. |
artwork object.
400 | Not a ZIP, no / several PDFs in it, unreadable PDF, several pages, RGB colourspace, no Illustrator variables and no variables map, a mapped text the PDF does not contain or a name it already binds, ink not in the colour catalogue, or bad formula / mutator_rules / variables. |
403 | Key is not owned by a staff account. |
404 | Artwork not found. |
409 | Not a master, or the uuid is not the current head of its lineage. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/package" \
-H "Authorization: Bearer sk_live_…" \
-F "file=@team-badge-package.zip" \
-F "variables={\"MISSION BEACH\": \"City\", \"CALIFORNIA\": \"State\"}"import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("team-badge-package.zip")]), "team-badge-package.zip");
form.append("variables", "{\"MISSION BEACH\": \"City\", \"CALIFORNIA\": \"State\"}");
const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/package", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" },
body: form
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/package",
headers={"Authorization": "Bearer sk_live_…"},
files={"file": open("team-badge-package.zip", "rb")},
data={
"variables": "{\"MISSION BEACH\": \"City\", \"CALIFORNIA\": \"State\"}",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/package");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('team-badge-package.zip'),
'variables' => '{"MISSION BEACH": "City", "CALIFORNIA": "State"}',
],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update artwork
Renames the artwork and/or replaces its description. Both fields optional; omitted fields are unchanged.
**Master templates (staff keys).** On an is_master artwork with a design document, staff keys may also send document (the edited schema-v1 document — recompiled exactly as creation does into a NEW artwork version: new uuid, derivatives re-pointed, live orders relinked; the response is the new version), variables (replaces the variable set — with document it names the new document's layers, without it the current document's; variables for dropped layers and the mutator rules bound to them are removed), mutator_rules (replaces the whole ordered waterfall), scope (+ customer for customer scope; moves the master between the house stock account and the customer) and categories (replaces the gallery category set). Validation is the same as on create. name renames the Design Lab idea too. A package master (uploaded Illustrator package, no document) takes name / description / status here but 409s on the document fields — replace its PDF with POST /v1/artwork/:uuid/package instead.
Required account permission: _Account_Artwork_Edit or _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
name | string | New unique name (≤ 255). Example Team Logo — 2026 season |
description | string | New description (≤ 65535). Example Front chest, 2-colour spot |
status | integer | Staff keys only. Manual status change with the SAME downstream effects as Admin → Artwork → Change Status: 2 (Pending Preflight) places the hold and creates the preflight contract / service request; 3 (Preflight Approved) releases the hold, emails the owner "preflight approved" and pushes every Pending Artwork order carrying this artwork into production; 4 records the failure; 1 / 5 / 6 are set with their hold bookkeeping. Each change writes a status event attributed to the key user, mirrored onto open orders, and emits the preflight / approval webhooks. A staff key without customer may address artwork on any account.One of: 1, 2, 3, 4, 5, 6 |
document | object | Staff keys only, masters only. Replacement schema-v1 design document (object, or JSON string). Recompiles into a new artwork version. |
variables | array | Staff keys only, masters only. Replacement [{layer_id, key}] set (same rules as create). |
mutator_rules | array | Staff keys only, masters only. Replacement ordered rule list (same rules as create). [] clears all rules. |
scope | string | Staff keys only, masters only. New gallery scope; customer takes the account from customer (or keeps the current owner when the master is already customer-owned).One of: global, customer |
categories | array of string | Staff keys only, masters only. Replacement gallery category names (created when new). [] clears. |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
artwork object.
400 | Validation / compile failed — see errors[]. |
403 | Customer key sent a staff-only field (status, document, …). |
404 | Artwork not found on this account. |
409 | Artwork name already taken; master field sent for a non-master or a master without a design document; the artwork is no longer the head of its lineage (reload). |
curl -X PATCH "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Team Logo — 2026 season",
"description": "Front chest, 2-colour spot",
"customer": "customer@example.com"
}'const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PATCH",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"name": "Team Logo — 2026 season",
"description": "Front chest, 2-colour spot",
"customer": "customer@example.com"
})
});
const { data, errors } = await response.json();import requests
response = requests.patch(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
json={
"name": "Team Logo — 2026 season",
"description": "Front chest, 2-colour spot",
"customer": "customer@example.com",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Team Logo — 2026 season',
'description' => 'Front chest, 2-colour spot',
'customer' => 'customer@example.com',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete master (staff)
Staff keys. Permanently deletes a master template that has never been ordered or printed, along with its derivatives when THEY have never been ordered or printed. Every version row of the master and of each current derivative is checked: on an order line, in print production (print images), in an open preflight / art-service request, in retain inventory, or on an ordered sheet → 409 listing every blocker, and nothing is deleted. Otherwise all lineage rows are hard-deleted in one transaction (print rotations, variables, mutator rules, print-file rows, pivots), the master Design row leaves the idea gallery (customer designs forked from it survive, unlinked), and an audit event is written. Stored files are not removed. Ordinary (non-master) artwork cannot be deleted — archive it.
Staff only — requires admin-console permission: _AdminConsole_Artwork_Edit
uuidrequired | string |
{deleted: 1, uuid, versions_deleted, derivatives_deleted, design_uuid}
403 | Key is not owned by a staff account. |
404 | Artwork not found. |
409 | Not a master, or the master / a derivative is ordered, printed, in review, in retain inventory or on an ordered sheet — errors[0].message lists the blockers. |
curl -X DELETE "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve artwork
Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
artwork object.
404 | Artwork not found on this account. |
curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Upload artwork
Creates a new artwork from an uploaded print file, exactly as the website's Art Library → Add Artwork flow does. The artwork is returned with status 5 (Preflight Required) and is not preflighted until it is ordered — POST /v1/orders submits it and the order waits in status 10 (Pending Artwork) until preflight passes. Pass preflight=1 to queue preflight immediately instead (the "Queue for early preflight" option on the website). Preflight completes asynchronously — subscribe to artwork.preflight_passed / artwork.preflight_failed or poll GET /v1/artwork/:uuid. For Spot artwork every PMS colour used in the file must be supplied via spot_color[] (name or Pantone code). Staff keys may additionally send customer to create the artwork on that customer's account (as Admin → Artwork → Create does) and status to create it in any status: 2 (Pending Preflight) submits it to preflight at once, creating the preflight contract and service request exactly as an order would; 1 / 3 / 4 / 6 are written directly with an audit event and without a preflight run — use 3 when importing artwork approved elsewhere, then attach its print-ready file with the staff production-file resource. Customer keys sending either field receive 403. artwork.created is emitted to the owning account's webhooks in every case. Send as multipart/form-data.
**Master templates (staff keys).** Send master=1 with document — a schema-v1 Design Lab document (the same JSON the design compiler's --compile takes: {schema_version:1, design:{artboard:{width_in,height_in}, layers:[…], colors:[{ref,pms,c,m,y,k}], fonts:[{ref,font}], distress?}}) — instead of file / type / width / height. The document is compiled through the design compiler exactly as Admin → Designs → Convert to Master does and becomes a fully usable master: an is_master artwork (Spot, Preflight Approved, print colours + press rotations from the compiled separations, hidden backer plates when inks touch, a Package archive), a master Design row in the Design Lab idea gallery under scope / categories, one variable per text layer named in variables (what derivatives substitute), and the mutator_rules waterfall. Every fonts[].font must name an installed face (<Family>-<Style>, exactly as the font library lists it); unknown faces are listed in the 400. name is required (≤ 128); formula optional (default Formula-1). Omit type / width / height / file / spot_color. The master block in the response carries design_uuid, variables and rules. Edit later with PATCH (document recompiles into a new version), read the stored document with GET /v1/artwork/:uuid/document, enumerate mutators with GET /v1/artwork/mutators, delete with DELETE /v1/artwork/:uuid.
**Package masters (staff keys).** Send master=1 with file = an Illustrator Package ZIP (File → Package: the master PDF beside its Fonts/ and Links/ folders, wrapper folder allowed) and NO document — the API twin of Admin → Artwork → Create Master Template → Upload Master Package. The PDF is stored **byte for byte** (nothing is recompiled, no backer is grafted, any distress mask is whatever the file carries): preflight vets it (exactly one page, CMYK/Spot inks only, at least one Illustrator variable bound to text), the separations become the print colours and press rotations (CMYK + an HA Underbase spot = CMYK; CMYK alone = CMYK 4 Lights; otherwise Spot — every ink must name a catalogue colour or the upload is 400 listing the misses), the Illustrator variables become the variable set ({trait, default} per field), mutator_rules are validated against those names, fonts under Fonts/ are ingested and Links/ recorded. The result is a master with no design document, so GET …/document and PATCH {document} are 409 for it; replace its package with POST /v1/artwork/:uuid/package.
**Binding text as variables (package masters).** A PDF that was edited outside Illustrator — a downloaded master whose text a tool replaced, a file exported by another application — usually carries NO Illustrator variables, and the upload is refused for that reason (the message lists the text the PDF does contain). Send variables as a JSON object of **the text exactly as it appears in the art → the variable name**, e.g. {"MISSION BEACH": "City", "CALIFORNIA": "State"}, and the API binds those text objects as Illustrator variables in the PDF before anything else runs — the same step the Design Lab's Convert to Master table performs when it asks which detected text is which variable. Each text is matched whole (trimmed, case-sensitive); every object showing it is bound; the variable's default value is the text itself. A text the PDF does not contain is a 400 that quotes the text it does; a name the PDF already binds as a variable is a 400 (drop or rename it). A PDF that already carries its variables needs no map, and may take one for additional text. This is the one case where the stored PDF differs from the upload — by the variable library and bindings only.
Required account permission: _Account_Artwork_Create or _AdminConsole_Artwork_Edit
| Field | Type | Description |
|---|---|---|
filerequired | file | The print file (≤ 50 MB). Vector PDF/AI/EPS preferred. With master=1: an Illustrator Package .zip (≤ 200 MB) creates a package master; omit it and send document for a lab master. |
namerequired | string | Unique name for this artwork on the owning account (≤ 255; ≤ 128 for a master). |
typerequired | string | Colour process. Not used when master=1 (masters are always Spot, from the compiled separations).One of: CMYK, CMYK4Lights, Spot |
formularequired | string | Ink formula. Optional for master=1 (default Formula-1).One of: Formula-1, Formula-2, Formula-4, Formula-5 |
widthrequired | decimal | Requested print width in inches (≤ 26.5). Not used when master=1 (artboard from the document). |
heightrequired | decimal | Requested print height in inches (≤ 19.25, or 19.5 when alternate size C is enabled on the owning account). Not used when master=1. |
description | string | Optional notes (≤ 65535). |
spot_color | array of string | Required for Spot. One entry per print colour: a colour name or Pantone code, e.g. spot_color[]=286 C. Not used when master=1. |
preflight | integer | 1 to queue preflight now (status 2) rather than when first ordered. Ignored when a staff key sends status.One of: 0, 1 · Default 0 |
customer | string | Staff keys only. Customer account to create the artwork on: the account's user uuid or sign-in email (a sub-user resolves to its account root). Omit to create on the key's own account. For master=1 this is the customer a customer-scoped master belongs to (and whose library artwork the document may place); omit for a global master. |
status | integer | Staff keys only. Status to create the artwork in: 2 creates the preflight contract + service request; 1 / 3 / 4 / 6 are set directly; 5 is the customer-upload default. Ignored for master=1 (masters are created Preflight Approved).One of: 1, 2, 3, 4, 5, 6 · Default 5 |
master | integer | Staff keys only. 1 creates a master template: from document (lab master) or from a package .zip in file (package master) — one or the other, never both.One of: 0, 1 · Default 0 |
document | string | Staff keys only, with master=1 and no file. The schema-v1 design document as a JSON string (≤ 128 MB). At least one layer; artboard.width_in / height_in in inches; every fonts[].font an installed face; an optional distress.uuid must exist. |
variables | string | Staff keys only, with master=1 + document. JSON array [{"layer_id": "<text layer id>", "key": "<VariableName>"}] — the text layers derivatives substitute. Each layer_id must be a non-blank text layer of the document and may appear once; each key (≤ 64) may bind one layer. Layers not listed stay static text. With master=1 + file (package master): a JSON OBJECT of text → variable name, e.g. {"MISSION BEACH": "City", "CALIFORNIA": "State"}, binding the PDF's text objects showing that text as Illustrator variables (see "Binding text as variables"). Optional when the PDF already carries its variables. |
design_lab_optout | integer | Staff keys only, package masters only. 1 keeps the master out of the Design Lab conversion sweep (the "Design Lab opt-out" box on the admin form). One of: 0, 1 · Default 0 |
mutator_rules | string | Staff keys only, with master=1. JSON array [{"mutator": "<name>", "variable_in": "<key>", "param": {…}}], applied in order to every derivative data row. mutator must be a registered name (GET /v1/artwork/mutators); param is validated against that mutator's params (omitted keys take defaults); variable_in must be a declared variable or a field an earlier rule produced (CreateField creates its variable_in). |
scope | string | Staff keys only, with master=1. Gallery scope. Default customer when customer is sent, else global. customer requires customer.One of: global, customer |
categories | string | Staff keys only, with master=1. JSON array of gallery category names (created when new), or a comma-separated list. |
artwork object.
400 | Validation failed — see errors[] (lab masters: malformed document, unknown font faces, bad variables / mutator rules, compile failure; package masters: not a ZIP, no / several PDFs, unreadable PDF, multi-page, RGB, no variables and no variables map, a mapped text the PDF does not contain or a name it already binds, ink not in the colour catalogue). |
403 | Customer key sent a staff-only field (customer, status, master, …). |
404 | customer does not name a customer account. |
409 | Artwork name already taken on the owning account. |
curl "https://api.heatapplied.com/v1/artwork" \
-H "Authorization: Bearer sk_live_…" \
-F "file=@logo.pdf" \
-F "name=Team Logo" \
-F "type=Spot" \
-F "formula=Formula-1" \
-F "width=10" \
-F "height=4" \
-F "spot_color[]=286 C" \
-F "spot_color[]=White"import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("logo.pdf")]), "logo.pdf");
form.append("name", "Team Logo");
form.append("type", "Spot");
form.append("formula", "Formula-1");
form.append("width", "10");
form.append("height", "4");
form.append("spot_color[]", "286 C");
form.append("spot_color[]", "White");
const response = await fetch("https://api.heatapplied.com/v1/artwork", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" },
body: form
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/artwork",
headers={"Authorization": "Bearer sk_live_…"},
files={"file": open("logo.pdf", "rb")},
data={
"name": "Team Logo",
"type": "Spot",
"formula": "Formula-1",
"width": 10,
"height": 4,
"spot_color[]": [
"286 C",
"White",
],
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('logo.pdf'),
'name' => 'Team Logo',
'type' => 'Spot',
'formula' => 'Formula-1',
'width' => 10,
'height' => 4,
'spot_color[0]' => '286 C',
'spot_color[1]' => 'White',
],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List artwork
Returns the latest version of every artwork on the account, newest first by default. Archived artwork is excluded unless archived=1. Use query to search name, TIN, description and formula.
Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List
| Field | Type | Description |
|---|---|---|
page | integer | 1-based page number. Default 1 |
per_page | integer | Rows per page, 1–100. Default 24 |
query | string | Free-text search across name, TIN, description and formula. |
archived | boolean | When true, list archived artwork instead of active artwork. Default |
master | boolean | When true, list master templates only. For a staff key without customer this lists masters on EVERY account (global masters belong to the house stock account, not the key's).Default |
sort | string | One of: updated_at, created_at, name, tin · Default updated_at |
order | string | One of: asc, desc · Default desc |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of artwork objects in data.
curl "https://api.heatapplied.com/v1/artwork?query=logo&per_page=10" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/artwork?query=logo&per_page=10", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/artwork",
headers={"Authorization": "Bearer sk_live_…"},
params={
"query": "logo",
"per_page": 10,
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/artwork?query=logo&per_page=10");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Sheet bundles (sheet runs) — create, read, edit and delete a printable sheet made up of placed artwork.
List sheet sizes
The sheet sizes a bundle can be built on. Each entry gives the physical sheet, the smaller printable area designs are placed within, and required_gap — the space the cutter needs between adjacent pieces, which the server already accounts for when it arranges a sheet. Call this before creating a bundle rather than hardcoding sizes.
Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
{sheet, physical:{width,height}, printable:{width,height}, required_gap}[]
curl "https://api.heatapplied.com/v1/bundles/sheets" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles/sheets", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/bundles/sheets",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles/sheets");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update a bundle
Changes a sheet's designs, size or name. Sending designs REPLACES the sheet's contents and re-arranges them — it is not a merge; omit it to keep them. As with create, check overflow, or send placements on every design to impose the sheet yourself. A sheet that is already on an order cannot be changed.
Required account permission: _Account_Cart_Create or _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
designs | array | The artwork to place: [{"artwork": "<uuid>", "number_up": 4}]. A bare uuid string is accepted and means number_up: 1. Every design must be in the owning account's library, and all designs on a sheet must share a compatible formula. Required on create; on update it REPLACES the sheet's contents. **To impose the sheet yourself**, give each design a placements list with one entry per copy — {"artwork": "<uuid>", "placements": [{"x": 0, "y": 0}, {"x": 6.5, "y": 0, "rotated": true}]} — x/y in inches from the printable-area top-left to the piece's top-left corner, rotated for a 90° turn; number_up then defaults to the list length. Supply placements for every design or for none. The piece size is taken from the artwork, not from you. The finished sheet must pass the same rules the Sheet Run Builder enforces: every piece inside the printable area, at least required_gap (see GET /v1/bundles/sheets) between pieces, and the whole sheet cuttable with straight edge-to-edge guillotine cuts — otherwise the request is rejected with every violation listed. |
sheet | string | Sheet size key — see GET /v1/bundles/sheets.Default 28x20 |
name | string | Sheet name. Default Untitled Bundle |
layout | object | A raw layout blob in the stored sheet format, instead of letting the server arrange the designs. Prefer designs[].placements — it needs only coordinates. Whatever you send is checked against the sheet run rules (inside the printable area, required gap, guillotine-cuttable, placements matching designs) and rejected with 400 listing every violation. |
scope | string | Staff only. global builds the sheet on the house stock account, where global master artwork lives.One of: global, customer |
customer | string | Staff only. Build on this customer account — uuid or sign-in email. |
bundle object.
400 | Same validation as create. |
403 | scope or customer sent by a customer key. |
404 | Bundle not found. |
409 | The sheet is on an order, or has been deleted. |
curl -X PATCH "https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PATCH",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.patch(
"https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete a bundle
Deletes a sheet and retires the hidden kit product behind it. Refused when the sheet is on an order, or while a design on it is still in preflight or art-service review — the reason comes back in errors[].
Required account permission: _Account_Cart_Create or _AdminConsole_Artwork_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
{deleted: 1, uuid}
404 | Bundle not found. |
409 | On an order, mid-preflight, or already deleted. |
curl -X DELETE "https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a bundle
One sheet with its designs, arrangement and status.
Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
bundle object.
404 | Bundle not found (or not on this account). |
curl "https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a bundle
Creates a sheet from a set of designs and arranges them on it. By default the server places the designs largest-first, rotating where it helps and leaving the cutter's gap, and the result is always guillotine-cuttable. **Check overflow on the response** — designs that did not fit are listed there and are NOT on the sheet; the call still succeeds with everything that did fit. Alternatively, pre-calculate the imposition yourself by sending placements on every design: the pieces go exactly where you put them, nothing overflows, and the sheet is rejected (400, every violation listed) if it breaks a sheet run rule. Saving also builds the hidden kit product that makes the sheet orderable and priceable.
Required account permission: _Account_Cart_Create or _AdminConsole_Artwork_Edit
| Field | Type | Description |
|---|---|---|
designs | array | The artwork to place: [{"artwork": "<uuid>", "number_up": 4}]. A bare uuid string is accepted and means number_up: 1. Every design must be in the owning account's library, and all designs on a sheet must share a compatible formula. Required on create; on update it REPLACES the sheet's contents. **To impose the sheet yourself**, give each design a placements list with one entry per copy — {"artwork": "<uuid>", "placements": [{"x": 0, "y": 0}, {"x": 6.5, "y": 0, "rotated": true}]} — x/y in inches from the printable-area top-left to the piece's top-left corner, rotated for a 90° turn; number_up then defaults to the list length. Supply placements for every design or for none. The piece size is taken from the artwork, not from you. The finished sheet must pass the same rules the Sheet Run Builder enforces: every piece inside the printable area, at least required_gap (see GET /v1/bundles/sheets) between pieces, and the whole sheet cuttable with straight edge-to-edge guillotine cuts — otherwise the request is rejected with every violation listed. |
sheet | string | Sheet size key — see GET /v1/bundles/sheets.Default 28x20 |
name | string | Sheet name. Default Untitled Bundle |
layout | object | A raw layout blob in the stored sheet format, instead of letting the server arrange the designs. Prefer designs[].placements — it needs only coordinates. Whatever you send is checked against the sheet run rules (inside the printable area, required gap, guillotine-cuttable, placements matching designs) and rejected with 400 listing every violation. |
scope | string | Staff only. global builds the sheet on the house stock account, where global master artwork lives.One of: global, customer |
customer | string | Staff only. Build on this customer account — uuid or sign-in email. |
bundle object.
400 | No designs, an unknown sheet size, a design that is not in the account's library, mixed formulas, a design with no recorded artboard size, placements on some designs but not all, a malformed placement, or a sheet that breaks the sheet run rules (piece outside the printable area, pieces closer than the required gap, not guillotine-cuttable, placements not matching the designs). |
403 | scope or customer sent by a customer key. |
curl -X POST "https://api.heatapplied.com/v1/bundles" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/bundles",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List bundles
Sheets on the account, newest first. Deleted sheets are hidden unless you ask for them with status=deleted.
Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 24 |
query | string | Matches the sheet name. |
status | string | Only sheets in this state. One of: draft, saved, ordered, deleted |
sort | string | One of: updated_at, created_at, name · Default updated_at |
order | string | One of: asc, desc · Default desc |
scope | string | Staff only. global lists the house stock account's sheets.One of: global, customer |
customer | string | Staff only. List this customer's sheets (uuid or email). |
Paginated list of bundle objects in data.
curl "https://api.heatapplied.com/v1/bundles" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/bundles", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/bundles",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/bundles");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Colors endpoint — /v1/colors/pantone
List Pantone inks
The ink catalogue — every ink the platform can print, with hex/RGB/CMYK values. An ink is listed when it carries a Pantone code or a name; pms holds whichever it has (code first, name as the fallback), so it is always populated and is the key to match on. Sorted by that same label.
**Cheap refresh** — the response's revision is a fingerprint of the full catalogue and is also sent as an ETag header. Send it back as If-None-Match on your next pull: an unchanged catalogue returns 304 Not Modified with no body. Alternatively pass ?updated_since=<ISO 8601> to receive only rows changed after that moment; revision and count always describe the full set, so a mismatch against your mirror tells you to do a full pull (e.g. after a deletion).
Required account permission: None (any valid API key)
| Field | Type | Description |
|---|---|---|
updated_since | string | ISO 8601 timestamp; only inks updated strictly after this moment are returned. Example 2026-09-08T00:00:00Z |
Object {revision, updated_at, count, colors: [pantone_color]} — revision (fingerprint) and count cover the full catalogue regardless of updated_since; updated_at is the newest row stamp in the full catalogue.
400 | Unparseable updated_since value. |
curl "https://api.heatapplied.com/v1/colors/pantone" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/colors/pantone", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/colors/pantone",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/colors/pantone");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]{
"revision": "9f2c1c6c1e0a4b7d8e3f5a6b7c8d9e0f1a2b3c4d",
"updated_at": "2026-09-01T14:03:22Z",
"count": 214,
"colors": [{
"id": 8612, "name": "Red", "pms": "185 C", "hex": "#E4002B",
"rgb": {"r": 228, "g": 0, "b": 43},
"cmyk": {"c": 0, "m": 100, "y": 81, "k": 4},
"is_special": false, "is_semiopaque": false,
"updated_at": "2026-09-01T14:03:22Z"
}],
"errors": []
}/v1/customers — staff-only customer account directory: search / list, read, create, update.
Update a customer account
Partial update of the account fields and / or the default contact, as Admin → Customers → Edit. Omitted fields are unchanged; contact merges field by field. Records a customer.updated event attributed to the key user. Status may move between Active, Inactive and Lead; delinquency is managed by billing, not here.
Staff only — requires admin-console permission: _AdminConsole_Customer_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
email | string | New sign-in email; must not belong to another user. |
organization | string | |
contact | contact | Any subset of the contact fields. |
status | integer | One of: 1, 3, 5 |
website | string | null | |
tax_exempt | boolean | |
notes | string | null |
customer object.
400 | Malformed field or empty required value. |
403 | Key is not a staff key or lacks _AdminConsole_Customer_Edit. |
404 | No customer account matches. |
409 | Email belongs to another user. |
curl -X PATCH "https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PATCH",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.patch(
"https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a customer account
Full account record including staff notes and every sign-in (root + sub-users). The uuid may be the root's or any sub-user's — a sub-user resolves to its account. A sign-in email is accepted in place of the uuid.
Staff only — requires admin-console permission: _AdminConsole_Customer_List
uuidrequired | string |
customer object.
403 | Key is not a staff key or lacks _AdminConsole_Customer_List. |
404 | No customer account matches. |
curl "https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/customers/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a customer account
Creates a root customer account with its default contact, exactly as Admin → Customers → Create does: the account gets the customer role, a customer.created event attributed to the key user, and no password (send send_email: true to email a set-password link when the account is Active). An existing Lead placeholder with the same email is upgraded in place; any other existing email is 409. Supports Lead accounts (status: 5) for prospects captured from a conversation.
Staff only — requires admin-console permission: _AdminConsole_Customer_Create
| Field | Type | Description |
|---|---|---|
emailrequired | string | Sign-in email; unique across all users. |
organizationrequired | string | |
contactrequired | contact | name is required; address / phone fields optional. email_address defaults to the sign-in email. |
status | integer | 1 Active, 3 Inactive, 5 Lead. One of: 1, 3, 5 · Default 1 |
website | string | |
tax_exempt | boolean | Default |
notes | string | |
send_email | boolean | Email the customer a set-password link (Active accounts only). Default |
customer object.
400 | Missing / malformed fields. |
403 | Key is not a staff key or lacks _AdminConsole_Customer_Create. |
409 | A non-lead user already exists with that email. |
curl "https://api.heatapplied.com/v1/customers" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"email": "<email>",
"organization": "<organization>",
"contact": {}
}'const response = await fetch("https://api.heatapplied.com/v1/customers", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"email": "<email>",
"organization": "<organization>",
"contact": {}
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/customers",
headers={"Authorization": "Bearer sk_live_…"},
json={
"email": "<email>",
"organization": "<organization>",
"contact": {},
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/customers");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'email' => '<email>',
'organization' => '<organization>',
'contact' => [],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Search / list customer accounts
Root customer accounts (sub-users are folded into their account), newest-updated first. query is the lookup an agent needs to resolve a name heard in conversation to an account: it matches the organization, the sign-in email, any sub-user email, and the default contact's name, email, phone and mobile (digits-only phone matching). Pass the returned uuid as customer on the Orders / Invoices / Artwork / Shipments resources to act for that account.
Staff only — requires admin-console permission: _AdminConsole_Customer_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 24 |
query | string | Organization, email, contact name or phone number fragment. |
status | string | Comma-separated status codes (e.g. 1,2). |
sort | string | One of: created_at, updated_at, organization, email, status · Default updated_at |
order | string | One of: asc, desc · Default desc |
Paginated list of customer objects in data.
403 | Key is not a staff key or lacks _AdminConsole_Customer_List. |
curl "https://api.heatapplied.com/v1/customers?query=acme" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/customers?query=acme", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/customers",
headers={"Authorization": "Bearer sk_live_…"},
params={
"query": "acme",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/customers?query=acme");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]{
"data": [{"uuid": "018e…", "email": "buyer@acme.com", "organization": "Acme Apparel", "status": 1, "status_text": "Active", "contact": {"name": "Pat Example", "phone_number": "+1 555 010 2000", "…": "…"}, "order_count": 42, "artwork_count": 118, "created_at": "2024-03-01 15:02:11", "updated_at": "2026-10-01 09:14:40"}],
"page": 1,
"pages": 1,
"total": 1,
"errors": []
}API v1 endpoint for die-cut nesting requests.
Unarchive a nesting request
Required account permission: _Account_DieCutNesting_Edit
uuidrequired | string |
curl -X POST "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/unarchive");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Archive a nesting request
Required account permission: _Account_DieCutNesting_Edit
uuidrequired | string |
curl -X POST "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/archive");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Download the nested output
Required account permission: _Account_DieCutNesting_Download
uuidrequired | string |
Binary file body.
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Download an input file
Required account permission: _Account_DieCutNesting_Download
uuidrequired | string |
fuuidrequired | string |
Binary file body.
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Retrieve an input file
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
fuuidrequired | string |
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List input files
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a nesting request
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
404 | Request not found |
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a nesting request
Multipart upload of PDF artwork files plus a template uuid; imposition runs asynchronously after the response is returned.
Required account permission: _Account_DieCutNesting_Create
curl -X POST "https://api.heatapplied.com/v1/die-cut-nesting/requests" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/die-cut-nesting/requests",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List nesting requests
Paginated die-cut nesting jobs (page, per_page).
Required account permission: _Account_DieCutNesting_List
curl "https://api.heatapplied.com/v1/die-cut-nesting/requests" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/requests", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/requests",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/requests");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API endpoint for die-cut nesting template management (v1).
Download the template file
Required account permission: _Account_DieCutNesting_Download
uuidrequired | string |
Binary file body.
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Download a template file
Required account permission: _Account_DieCutNesting_Download
uuidrequired | string |
fuuidrequired | string |
Binary file body.
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Retrieve a template file
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
fuuidrequired | string |
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete a template file
Required account permission: _Account_DieCutNesting_Edit
uuidrequired | string |
fuuidrequired | string |
curl -X DELETE "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List template files
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete a template
Required account permission: _Account_DieCutNesting_Delete
uuidrequired | string |
404 | Template not found |
curl -X DELETE "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a template
Required account permission: _Account_DieCutNesting_List
uuidrequired | string |
404 | Template not found |
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update a template
Required account permission: _Account_DieCutNesting_Edit
uuidrequired | string |
404 | Template not found |
curl -X PUT "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PUT",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.put(
"https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a template
Required account permission: _Account_DieCutNesting_Create
curl -X POST "https://api.heatapplied.com/v1/die-cut-nesting/templates" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/die-cut-nesting/templates",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List templates
Paginated die-cut nesting templates (block positions used by imposition).
Required account permission: _Account_DieCutNesting_List
curl "https://api.heatapplied.com/v1/die-cut-nesting/templates" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/die-cut-nesting/templates", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/die-cut-nesting/templates",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/die-cut-nesting/templates");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Fonts endpoint — /v1/fonts
List designer-visible fonts
The curated font catalogue — every typeface selectable in the Artwork Designer, with its canonical name, print file name and woff2 URL. Sorted by family, then style.
This is the canonical machine-readable source of the visible set: the S3 font/ prefix and cli font mirrors intentionally retain HIDDEN fonts (culled or license-flagged rows keep their files so existing artwork keeps rendering), so a bucket listing always overcounts.
**Cheap refresh** — the response's revision is a fingerprint of the full catalogue and is also sent as an ETag header. Send it back as If-None-Match on your next pull: an unchanged catalogue returns 304 Not Modified with no body. Alternatively pass ?updated_since=<ISO 8601> to receive only rows changed after that moment; revision and count always describe the full set, so a mismatch against your mirror tells you to do a full pull (e.g. after a font is hidden).
Required account permission: None (any valid API key)
| Field | Type | Description |
|---|---|---|
updated_since | string | ISO 8601 timestamp; only fonts updated strictly after this moment are returned. Example 2026-09-08T00:00:00Z |
Object {revision, updated_at, count, fonts: [font]} — revision (fingerprint) and count cover the full catalogue regardless of updated_since; updated_at is the newest row stamp in the full catalogue.
400 | Unparseable updated_since value. |
curl "https://api.heatapplied.com/v1/fonts" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/fonts", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/fonts",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/fonts");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]{
"revision": "9f2c1c6c1e0a4b7d8e3f5a6b7c8d9e0f1a2b3c4d",
"updated_at": "2026-09-09T20:08:14Z",
"count": 899,
"fonts": [{
"id": 368, "font": "Bebas Neue-Regular",
"family": "Bebas Neue", "style": "Regular",
"file_name": "Bebas Neue-Regular.ttf",
"woff2_url": "https://.../font/web/Bebas%20Neue-Regular.woff2",
"updated_at": "2026-09-09T20:08:14Z"
}],
"errors": []
}API v1 Invoices — list, retrieve and download invoices.
Download invoice PDF
Streams the invoice as application/pdf (regenerated on each request so status and payments are current).
Required account permission: _Account_Invoice_View or _AdminConsole_Invoice_View
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Binary PDF body with Content-Disposition: attachment.
404 | Invoice not found |
curl "https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
-H "Authorization: Bearer sk_live_…" \
-o invoice.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("invoice.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("invoice.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("invoice.pdf", curl_exec($ch));Retrieve an invoice
Required account permission: _Account_Invoice_View or _AdminConsole_Invoice_View
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
invoice object.
404 | Invoice not found |
curl "https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List invoices
Invoices for the account, newest first. Line items and transactions are omitted in the listing.
Required account permission: _Account_Invoice_List or _AdminConsole_Invoice_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 24 |
query | string | Matches invoice number (I000123), order number or your PO number. |
status | string | Comma-separated status codes, e.g. 1,5 for anything with a balance. |
order | string | Order uuid — only invoices for that order. |
sort | string | One of: created_at, updated_at, total_amount, full_id, status · Default created_at |
order_by | string | Sort direction (order is taken by the order-uuid filter).One of: asc, desc · Default desc |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of invoice objects in data.
curl "https://api.heatapplied.com/v1/invoices" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/invoices", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/invoices",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/invoices");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API v1 Orders — list, retrieve and create sales orders.
List shipments for an order
All shipments carrying the order, as {data: [shipment…]} (not paginated).
Required account permission: _Account_Shipment_List or _AdminConsole_Shipment_List
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of shipment objects in data.
404 | Order not found |
curl "https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/shipments" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/shipments", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/shipments",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/shipments");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List invoices for an order
All invoices for the order, as {data: [invoice…]} (not paginated).
Required account permission: _Account_Invoice_List or _AdminConsole_Invoice_List
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of invoice objects in data.
404 | Order not found |
curl "https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/invoices" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/invoices", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/invoices",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/invoices");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve an order
Full order detail including line items with artwork and services, shipment summaries with tracking, and invoice summaries with balance due.
Required account permission: _Account_Order_View or _AdminConsole_Order_View
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
order object.
404 | Order not found |
curl "https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create an order
Creates and (unless paying by terms) pays for a sales order in one call.
Recommended flow: POST /v1/pricing/quote per design → POST /v1/shipping/rates with the same ship_to and line_items → POST /v1/orders naming the chosen shipping.service and a payment.payment_method. Pricing is identical across all three calls.
Tax (4.3%) applies only to US/VA destinations for non-exempt accounts. Rush is honoured only for accounts with rush enabled and creates a rush service request for production review. Orders whose artwork is not yet preflight-approved start in status 10 (Pending Artwork) and move to 6 (In Production) automatically once approved.
Required account permission: _Account_Order_Create or _AdminConsole_Order_Edit
| Field | Type | Description |
|---|---|---|
customer_purchase_order | string | Your PO number (255 chars max). Echoed on the invoice. Example PO-10422 |
notes | string | Free-form production notes. |
ship_torequired | ship_to | |
line_itemsrequired | array of line_item_request | |
shippingrequired | shipping_request | |
payment | payment_request | Optional for accounts with payment terms (defaults to terms). |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
order object.
400 | Validation failure — errors[0].message names the offending field (e.g. line_items[0].artwork.uuid '…' not found). |
402 | Payment declined / could not be authorised. |
curl "https://api.heatapplied.com/v1/orders" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"customer_purchase_order": "PO-10422",
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234"
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…"
},
"quantity": 144,
"customer_sku": "TEE-LOGO-FRONT",
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White"
]
}
]
}
],
"shipping": {
"method": "rate",
"carrier": "UPS",
"service": "UPS GROUND"
},
"payment": {
"method": "payment_method",
"payment_method": "9b2e4c1a-…"
}
}'const response = await fetch("https://api.heatapplied.com/v1/orders", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"customer_purchase_order": "PO-10422",
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234"
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…"
},
"quantity": 144,
"customer_sku": "TEE-LOGO-FRONT",
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White"
]
}
]
}
],
"shipping": {
"method": "rate",
"carrier": "UPS",
"service": "UPS GROUND"
},
"payment": {
"method": "payment_method",
"payment_method": "9b2e4c1a-…"
}
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/orders",
headers={"Authorization": "Bearer sk_live_…"},
json={
"customer_purchase_order": "PO-10422",
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234",
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…",
},
"quantity": 144,
"customer_sku": "TEE-LOGO-FRONT",
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White",
],
},
],
},
],
"shipping": {
"method": "rate",
"carrier": "UPS",
"service": "UPS GROUND",
},
"payment": {
"method": "payment_method",
"payment_method": "9b2e4c1a-…",
},
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'customer_purchase_order' => 'PO-10422',
'ship_to' => [
'name' => 'Acme Screen Printing',
'street1' => '123 Main St',
'city' => 'Richmond',
'state' => 'VA',
'postal_code' => '23220',
'country' => 'US',
'email_address' => 'receiving@acme.example',
'phone_number' => '+18045551234',
],
'line_items' => [
[
'artwork' => [
'uuid' => '3f1c2a9e-…',
],
'quantity' => 144,
'customer_sku' => 'TEE-LOGO-FRONT',
'color_changes' => [
[
'quantity' => 100,
'colors' => [
'185 C',
'White',
],
],
],
],
],
'shipping' => [
'method' => 'rate',
'carrier' => 'UPS',
'service' => 'UPS GROUND',
],
'payment' => [
'method' => 'payment_method',
'payment_method' => '9b2e4c1a-…',
],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List orders
Paginated sales orders for the account, newest first. Line items, shipments and invoices are omitted in the listing — fetch GET /v1/orders/:uuid for detail.
Required account permission: _Account_Order_List or _AdminConsole_Order_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 24 |
query | string | Matches order number (S01234) or your PO number. |
status | string | Comma-separated status codes, e.g. 6,7. |
created_after | string | Y-m-d inclusive. |
created_before | string | Y-m-d inclusive. |
sort | string | One of: created_at, updated_at, ship_date, delivery_date, total_amount, full_id, status · Default created_at |
order | string | One of: asc, desc · Default desc |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of order objects in data.
curl "https://api.heatapplied.com/v1/orders?status=6%2C7&per_page=10" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/orders?status=6%2C7&per_page=10", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/orders",
headers={"Authorization": "Bearer sk_live_…"},
params={
"status": "6,7",
"per_page": 10,
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/orders?status=6%2C7&per_page=10");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API v1 Payment Methods — saved cards and bank accounts used by POST /v1/orders.
Start card setup
Returns the Stripe **publishable** key for this account's sales channel so your client can tokenise a card with Stripe.js / Stripe Elements (or POST https://api.stripe.com/v1/payment_methods authenticated with the publishable key). Card numbers go straight from the cardholder to Stripe — never to this API. Post the resulting pm_… id to POST /v1/payment-methods/cards. mode is test or live; in test mode use Stripe's test cards (e.g. 4242 4242 4242 4242).
Required account permission: _Account_PaymentMethod_Create
Object {provider: \"stripe\", publishable_key, mode}.
400 | No card-capable payment provider is configured for the sales channel. |
curl "https://api.heatapplied.com/v1/payment-methods/cards/setup" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/payment-methods/cards/setup", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/payment-methods/cards/setup",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/cards/setup");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Start bank account setup
Creates a Stripe SetupIntent for a US bank account and returns its client_secret. Complete it client-side with stripe.collectBankAccountForSetup() + stripe.confirmUsBankAccountSetup() (Financial Connections instant verification or micro-deposits), then call POST /v1/payment-methods/banks with the resulting pm_… id.
Required account permission: _Account_PaymentMethod_Create
Object {client_secret}.
curl -X POST "https://api.heatapplied.com/v1/payment-methods/banks/setup" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/payment-methods/banks/setup", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/payment-methods/banks/setup",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/banks/setup");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Save a bank account
Persists a bank account after the SetupIntent has been confirmed client-side. If micro-deposits were used the method is returned with status: pending and must be verified via POST /v1/payment-methods/:uuid/verify before it can be charged.
Required account permission: _Account_PaymentMethod_Create
| Field | Type | Description |
|---|---|---|
payment_method_idrequired | string | The pm_… id from the confirmed SetupIntent (setup_intent.payment_method). |
labelrequired | string | Friendly name, e.g. "Operating account". |
billing_contactrequired | billing_contact |
payment_method object.
400 | Missing field, limit reached, or the provider rejected the payment method. |
curl "https://api.heatapplied.com/v1/payment-methods/banks" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"payment_method_id": "<payment_method_id>",
"label": "<label>",
"billing_contact": {
"name": "<name>",
"street1": "<street1>",
"city": "<city>",
"state": "<state>",
"postal_code": "<postal_code>",
"country": "US",
"email_address": "<email_address>"
}
}'const response = await fetch("https://api.heatapplied.com/v1/payment-methods/banks", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"payment_method_id": "<payment_method_id>",
"label": "<label>",
"billing_contact": {
"name": "<name>",
"street1": "<street1>",
"city": "<city>",
"state": "<state>",
"postal_code": "<postal_code>",
"country": "US",
"email_address": "<email_address>"
}
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/payment-methods/banks",
headers={"Authorization": "Bearer sk_live_…"},
json={
"payment_method_id": "<payment_method_id>",
"label": "<label>",
"billing_contact": {
"name": "<name>",
"street1": "<street1>",
"city": "<city>",
"state": "<state>",
"postal_code": "<postal_code>",
"country": "US",
"email_address": "<email_address>",
},
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/banks");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'payment_method_id' => '<payment_method_id>',
'label' => '<label>',
'billing_contact' => [
'name' => '<name>',
'street1' => '<street1>',
'city' => '<city>',
'state' => '<state>',
'postal_code' => '<postal_code>',
'country' => 'US',
'email_address' => '<email_address>',
],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Save a card
Tokenise the card with Stripe.js / Stripe Elements using the publishable key from GET /v1/payment-methods/cards/setup, then post the resulting pm_… id here. The billing contact is stored as the card's billing address and used for AVS. Accounts are limited to 50 saved methods.
Required account permission: _Account_PaymentMethod_Create
| Field | Type | Description |
|---|---|---|
tokenrequired | string | Stripe PaymentMethod id (pm_…) or legacy token (tok_…) created client-side with the publishable key — never send raw card numbers. |
label | string | Friendly name. |
billing_contactrequired | billing_contact |
payment_method object.
400 | Missing field, limit reached, or the provider rejected the token (errors[0].message). |
curl "https://api.heatapplied.com/v1/payment-methods/cards" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"token": "pm_1Nxyz…",
"label": "Corporate Visa",
"billing_contact": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "ap@acme.example"
}
}'const response = await fetch("https://api.heatapplied.com/v1/payment-methods/cards", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"token": "pm_1Nxyz…",
"label": "Corporate Visa",
"billing_contact": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "ap@acme.example"
}
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/payment-methods/cards",
headers={"Authorization": "Bearer sk_live_…"},
json={
"token": "pm_1Nxyz…",
"label": "Corporate Visa",
"billing_contact": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "ap@acme.example",
},
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/cards");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'token' => 'pm_1Nxyz…',
'label' => 'Corporate Visa',
'billing_contact' => [
'name' => 'Acme Screen Printing',
'street1' => '123 Main St',
'city' => 'Richmond',
'state' => 'VA',
'postal_code' => '23220',
'country' => 'US',
'email_address' => 'ap@acme.example',
],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Verify a bank account
Submit the two micro-deposit amounts Stripe sent to the account, as integer cents ({"amount1": 32, "amount2": 45}). Decimal dollar values are rejected.
Required account permission: _Account_PaymentMethod_Create
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
amount1required | integer | First micro-deposit in cents (1–100), e.g. 32 for $0.32.Example 32 |
amount2required | integer | Second micro-deposit in cents (1–100). Example 45 |
payment_method object.
400 | Amounts not integer cents, out of range (1–100) or do not match. |
404 | Payment method not found |
curl "https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/verify" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"amount1": 32,
"amount2": 45
}'const response = await fetch("https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/verify", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"amount1": 32,
"amount2": 45
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/verify",
headers={"Authorization": "Bearer sk_live_…"},
json={
"amount1": 32,
"amount2": 45,
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/verify");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'amount1' => 32,
'amount2' => 45,
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Remove a payment method
Detaches the method at the provider and deletes the saved record. Orders already placed with it are unaffected.
Required account permission: _Account_PaymentMethod_Delete
uuidrequired | string |
Object {deleted: true}.
404 | Payment method not found |
curl -X DELETE "https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List payment methods
Saved cards and bank accounts plus whether the account may pay by terms or check. Pick a uuid with usable: true for payment.payment_method on POST /v1/orders.
Required account permission: _Account_PaymentMethod_List
payment_method_list object.
curl "https://api.heatapplied.com/v1/payment-methods" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/payment-methods", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/payment-methods",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/payment-methods");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Pricing — quote heat-transfer prices before you order.
Quote transfers
Prices one design at one quantity. Pass artwork (uuid) for an existing file or spec for a design you have not uploaded yet. The quote is binding for the same inputs at order time (shipping and tax excluded). A matched: null response means the design does not fit any transfer size — reduce width/height.
**Colour changes** — to run the same design in additional ink colourways, add color_changes[], each with its own quantity and a colors[] list giving the replacement ink for every swappable colour of the base artwork in the artwork's colors order. All colourways share one price break (total_quantity); each colourway under 300 pcs bills the colour-change fee once per changed ink. The same color_changes[] shape is accepted on POST /v1/orders line items.
Required account permission: _Account_Quote_Create or _AdminConsole_Order_Edit
| Field | Type | Description |
|---|---|---|
artwork | string | Artwork UUID (alternative to spec). |
spec | design_spec | Design specification (alternative to artwork). |
quantityrequired | integer | Base colourway quantity. |
rush_days | integer | 0–3. Default 0 |
color_changes | array of color_change | Additional colourways (see color_change). |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
quote object.
400 | Missing/invalid inputs, neither artwork nor spec given, or a color_changes[] entry that does not match the artwork's colour count / names an unknown ink. |
404 | Artwork not found on this account. |
curl "https://api.heatapplied.com/v1/pricing/quote" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"artwork": "3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
"quantity": 144,
"rush_days": 1,
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White"
]
}
]
}'const response = await fetch("https://api.heatapplied.com/v1/pricing/quote", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"artwork": "3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
"quantity": 144,
"rush_days": 1,
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White"
]
}
]
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/pricing/quote",
headers={"Authorization": "Bearer sk_live_…"},
json={
"artwork": "3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
"quantity": 144,
"rush_days": 1,
"color_changes": [
{
"quantity": 100,
"colors": [
"185 C",
"White",
],
},
],
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/pricing/quote");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'artwork' => '3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60',
'quantity' => 144,
'rush_days' => 1,
'color_changes' => [
[
'quantity' => 100,
'colors' => [
'185 C',
'White',
],
],
],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]{
"artwork": {"uuid": "3f1c2a9e-…", "name": "Team Logo", "tin": "HA111AL17083331"},
"matched": {"sku": "HTF1AL", "size_code": "AL", "max_width": 11.5, "max_height": 4.5, "rotated": 0, "utilization_percent": 61.8},
"quantity": 144, "total_quantity": 244, "currency": "USD",
"unit_price": 131, "subtotal": 31964,
"breakdown": [{"label": "Transfers — AL (11.5\" × 4.5\")", "quantity": 244, "unit_price": 119, "total_price": 28940}, {"label": "Screen setup (one-time)", "quantity": 1, "unit_price": 3024, "total_price": 3024}],
"color_changes": {"unit_fee": 2500, "minimum": 300, "colorways": [{"quantity": 100, "colors": [{"id": 8612, "name": "Red", "pms": "185 C", "hex": "#E4002B"}, {"id": 8501, "name": "White", "pms": null, "hex": "#FFFFFF"}], "changed": 1, "billable": 1, "fee": 2500}], "total_fee": 2500},
"rush": {"enabled": true, "days": 1, "unit_fee": 5000, "fee": 5000},
"total": 39464,
"tiers": [{"quantity": 72, "price_each": 186, "price_total": 13392}],
"turn_days": 2, "estimated_ship_date": "2026-08-27", "has_custom_pricing": false, "errors": []
}API v1 Shipments — read-only access to outbound shipments, their boxes and tracking.
Retrieve a shipment
One shipment by its P###### number with boxes, tracking numbers and tracking URLs.
Required account permission: _Account_Shipment_View or _AdminConsole_Shipment_View
idrequired | string |
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
shipment object.
404 | Shipment not found |
curl "https://api.heatapplied.com/v1/shipments/P123456" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/shipments/P123456", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/shipments/P123456",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/shipments/P123456");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List shipments
Shipments carrying any of the account's orders, newest first. Subscribe to shipment.created / shipment.tracking_updated / order.shipped webhooks instead of polling where possible.
Required account permission: _Account_Shipment_List or _AdminConsole_Shipment_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 24 |
status | string | Comma-separated status codes. |
order | string | Order uuid — only shipments for that order. |
tracking_number | string | Find the shipment containing a tracking number. |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
Paginated list of shipment objects in data.
curl "https://api.heatapplied.com/v1/shipments" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/shipments", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/shipments",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/shipments");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API v1 Shipping — carrier options and live UPS rate quotes ahead of POST /v1/orders.
Shipping options
Carriers, service names and which shipping.method values the account may use. Service names are matched case-insensitively on the order.
Required account permission: _Account_Order_Create or _AdminConsole_Order_Edit
| Field | Type | Description |
|---|---|---|
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
shipping_options object.
curl "https://api.heatapplied.com/v1/shipping/options" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/shipping/options", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/shipping/options",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/shipping/options");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Quote shipping rates
Live UPS rates for a destination and the line items you intend to order. Parcels are estimated from the artwork sheet sizes and quantities; pickup is the computed production ship date. The amount returned for a service is exactly what POST /v1/orders bills when you pass the same ship_to, line_items and shipping.service with shipping.method: rate.
Amounts are integer cents. Accounts with delayed billing see amount: 0. Rates are not stored — quote again if the cart changes.
Required account permission: _Account_Order_Create or _AdminConsole_Order_Edit
| Field | Type | Description |
|---|---|---|
ship_torequired | ship_to | |
line_itemsrequired | array of line_item_request | |
customer | string | Staff keys only. Act for this customer account (user uuid or sign-in email; a sub-user resolves to its account root). The request then reads and writes that account exactly as the customer's own key would. 404 when no customer account matches; 403 from a customer key. Example customer@example.com |
shipping_rates object.
400 | Validation failure or carrier rating error (errors[0].message). |
curl "https://api.heatapplied.com/v1/shipping/rates" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234"
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…"
},
"quantity": 144
}
]
}'const response = await fetch("https://api.heatapplied.com/v1/shipping/rates", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234"
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…"
},
"quantity": 144
}
]
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/shipping/rates",
headers={"Authorization": "Bearer sk_live_…"},
json={
"ship_to": {
"name": "Acme Screen Printing",
"street1": "123 Main St",
"city": "Richmond",
"state": "VA",
"postal_code": "23220",
"country": "US",
"email_address": "receiving@acme.example",
"phone_number": "+18045551234",
},
"line_items": [
{
"artwork": {
"uuid": "3f1c2a9e-…",
},
"quantity": 144,
},
],
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/shipping/rates");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'ship_to' => [
'name' => 'Acme Screen Printing',
'street1' => '123 Main St',
'city' => 'Richmond',
'state' => 'VA',
'postal_code' => '23220',
'country' => 'US',
'email_address' => 'receiving@acme.example',
'phone_number' => '+18045551234',
],
'line_items' => [
[
'artwork' => [
'uuid' => '3f1c2a9e-…',
],
'quantity' => 144,
],
],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Ping endpoint — /v1/ping
Liveness check
Returns the current server time. No authentication required; not written to the request log.
Required account permission: None (any valid API key)
Object {timestamp}.
curl "https://api.heatapplied.com/v1/ping" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/ping", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/ping",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/ping");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API endpoint for team event management (v1).
Delete a team event
Required account permission: _Account_Team_Delete
uuidrequired | string |
404 | Event not found |
curl -X DELETE "https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a team event
Required account permission: _Account_Team_List
uuidrequired | string |
404 | Event not found |
curl "https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update a team event
Required account permission: _Account_Team_Edit
uuidrequired | string |
404 | Event not found |
curl -X PUT "https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PUT",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.put(
"https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/events/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a team event
Required account permission: _Account_Team_Create
curl -X POST "https://api.heatapplied.com/v1/teams/events" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/events", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/teams/events",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/events");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List team events
Paginated team events (games, tournaments, fundraisers) for the account (page, per_page).
Required account permission: _Account_Team_List
curl "https://api.heatapplied.com/v1/teams/events" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/events", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/teams/events",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/events");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API endpoint for team roster management (v1).
Delete a roster
Required account permission: _Account_Team_Delete
uuidrequired | string |
404 | Roster not found |
curl -X DELETE "https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a roster
Required account permission: _Account_Team_List
uuidrequired | string |
404 | Roster not found |
curl "https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update a roster
Required account permission: _Account_Team_Edit
uuidrequired | string |
404 | Roster not found |
curl -X PUT "https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PUT",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.put(
"https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/rosters/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a roster
Creates a roster of members (name, number, sizes) attached to a team event.
Required account permission: _Account_Team_Create
curl -X POST "https://api.heatapplied.com/v1/teams/rosters" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/rosters", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/teams/rosters",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/rosters");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List rosters
Paginated rosters for the account (page, per_page).
Required account permission: _Account_Team_List
curl "https://api.heatapplied.com/v1/teams/rosters" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/teams/rosters", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/teams/rosters",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/teams/rosters");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]API v1 Tickets — full create / read / update / delete plus the ticket actions of Admin → Support → Tickets (reply, attachments, tags, followers, assignment, merge).
List ticket enumerations
Code → label maps for type, status, priority and (staff keys) classification, plus the subsets a customer key may set. Use it to render pickers without hard-coding the tables above.
Required account permission: _Account_Ticket_List or _AdminConsole_Ticket_List
{types, statuses, priorities, classifications?, customer_types, customer_statuses}
curl "https://api.heatapplied.com/v1/tickets/options" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/options", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/tickets/options",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/options");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List tags
Every tag in the catalogue, alphabetical. Tags categorise tickets and drive the knowledgebase (knowledgebase = guide card, faq = FAQ accordion, others = category chips).
Required account permission: _Account_Ticket_List or _AdminConsole_Ticket_List
Paginated list of ticket_tag objects in data.
curl "https://api.heatapplied.com/v1/tickets/tags" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/tags", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/tickets/tags",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/tags");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a tag
Staff keys. Adds a tag to the catalogue; returns the existing tag when the name is already taken.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
| Field | Type | Description |
|---|---|---|
namerequired | string | ≤ 64 characters. |
ticket_tag object.
400 | Missing or too-long name. |
curl "https://api.heatapplied.com/v1/tickets/tags" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "<name>"
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/tags", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"name": "<name>"
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/tags",
headers={"Authorization": "Bearer sk_live_…"},
json={
"name": "<name>",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/tags");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'name' => '<name>',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Download an attachment
Streams a ticket or reply attachment. Customer keys may download only files their account uploaded.
Required account permission: _Account_Ticket_Download or _AdminConsole_Ticket_Download
uuidrequired | string |
fuuidrequired | string |
Binary file body with Content-Disposition: attachment.
404 | Ticket or file not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download" \
-H "Authorization: Bearer sk_live_…" \
-o artwork.pdfimport fs from "node:fs";
const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
fs.writeFileSync("artwork.pdf", Buffer.from(await response.arrayBuffer()));import requests
response = requests.get(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download",
headers={"Authorization": "Bearer sk_live_…"},
)
with open("artwork.pdf", "wb") as f:
f.write(response.content)$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
file_put_contents("artwork.pdf", curl_exec($ch));Remove an attachment
Detaches the file from the ticket / reply and deletes it from storage (private bucket, or the public bucket for Article assets). Customer keys may remove only files their account uploaded.
Required account permission: _Account_Ticket_Edit or _AdminConsole_Ticket_Edit
uuidrequired | string |
fuuidrequired | string |
ticket object.
404 | Ticket or file not found. |
curl -X DELETE "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Attach files
Uploads one or more attachments to the ticket (or to one of its replies when message is given). Send as multipart/form-data with file or file[]. Support-ticket files are private (download via the authenticated endpoint). Files attached directly to a knowledgebase Article (type: 1, no message) are stored publicly under knowledgebase/<ticket uuid>/<name> and each DTO carries its public url; the article body references them by bare filename. Filenames pass through a safe-name filter — use the name returned in files[].
Required account permission: _Account_Ticket_Reply or _AdminConsole_Ticket_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
filerequired | file | One or more files (≤ 50 MB each). |
message | string | Reply uuid to attach to instead of the ticket. |
ticket object.
400 | No file / file too large. |
404 | Ticket or message not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files" \
-H "Authorization: Bearer sk_live_…" \
-F "file=@file.pdf"import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("file.pdf")]), "file.pdf");
const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" },
body: form
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files",
headers={"Authorization": "Bearer sk_live_…"},
files={"file": open("file.pdf", "rb")},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('file.pdf'),
],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Edit a reply
Replaces the body of a reply. Customer keys may edit only messages their account sent.
Required account permission: _Account_Ticket_Edit or _AdminConsole_Ticket_Edit
uuidrequired | string |
muuidrequired | string |
| Field | Type | Description |
|---|---|---|
bodyrequired | string |
ticket object.
403 | Not the author (customer keys). |
404 | Ticket or message not found. |
curl -X PATCH "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"body": "<body>"
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}", {
method: "PATCH",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"body": "<body>"
})
});
const { data, errors } = await response.json();import requests
response = requests.patch(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}",
headers={"Authorization": "Bearer sk_live_…"},
json={
"body": "<body>",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'body' => '<body>',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete a reply
Removes a reply from the thread. Customer keys may delete only messages their account sent.
Required account permission: _Account_Ticket_Delete or _AdminConsole_Ticket_Delete
uuidrequired | string |
muuidrequired | string |
ticket object.
403 | Not the author (customer keys). |
404 | Ticket or message not found. |
curl -X DELETE "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages/{muuid}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Reply to a ticket
Appends a reply to the thread as the key user and auto-follows the ticket. Staff replies on a ticket that backs a customer conversation are mirrored to the customer's inbox and emailed to them (conversation-message-customer); every reply emails the other participants (ticket-response). Attach files afterwards with POST /v1/tickets/:uuid/files using message=<message uuid>.
Required account permission: _Account_Ticket_Reply or _AdminConsole_Ticket_View
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
bodyrequired | string | Reply HTML (≤ 256 KB). |
ticket object.
400 | Empty body. |
404 | Ticket not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"body": "<body>"
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"body": "<body>"
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages",
headers={"Authorization": "Bearer sk_live_…"},
json={
"body": "<body>",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/messages");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'body' => '<body>',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Remove a follower
Removes one user from the follower list — the × beside a follower in the admin console. :user is the follower's uuid or sign-in email address. Other participants (assignee, project manager, creator) are unaffected.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
uuidrequired | string |
userrequired | string |
ticket object.
403 | Customer key. |
404 | Ticket not found, user not found, or user is not following this ticket. |
curl -X DELETE "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers/{user}" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers/{user}", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers/{user}",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers/{user}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Add followers
Adds one or more staff / customer users to the ticket's follower list without disturbing the existing followers — the admin console's Followers picker. Followers receive the ticket-updated and ticket-response notifications and can see the ticket when it is private. Users already following are skipped, so the call is idempotent. Identify users by uuid or by the email address they sign in with. Nobody is emailed by this call.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
usersrequired | array of string | User uuids or sign-in email addresses to add. A single string is accepted too. |
ticket object.
400 | No users given, or a user was not found. |
403 | Customer key. |
404 | Ticket not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"users": []
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"users": []
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers",
headers={"Authorization": "Bearer sk_live_…"},
json={
"users": {},
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/followers");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'users' => [],
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Unassign a ticket
Clears the assignee and delegator and removes their calendar events — the admin console's Remove Assignee action. Succeeds (no-op) when the ticket is already unassigned.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
uuidrequired | string |
ticket object.
403 | Customer key. |
404 | Ticket not found. |
curl -X DELETE "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Assign a ticket
Assigns the ticket to one staff member — the admin console's Assignee picker. A ticket has a single assignee: assigning replaces the current one. The key user is recorded as delegator (unless assigning to themselves), the assignee's and delegator's calendar events are rebuilt from due_at, and the new assignee is emailed with the ticket-updated notification. Use followers for additional people who should be kept in the loop.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
userrequired | string | User uuid or sign-in email address. |
ticket object.
400 | No user given, or user not found. |
403 | Customer key. |
404 | Ticket not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"user": "<user>"
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"user": "<user>"
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee",
headers={"Authorization": "Bearer sk_live_…"},
json={
"user": "<user>",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/assignee");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'user' => '<user>',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Merge tickets
Staff keys. Merges target into this ticket (direction: "that_into_this", default) or this ticket into target ("this_into_that"), exactly as the admin console's Merge action: the master keeps its values and fills blanks from the child, the child's summary, replies, followers and tags move to the master, and the child is Closed under the master with a "merged with" note.
Staff only — requires admin-console permission: _AdminConsole_Ticket_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
targetrequired | string | Uuid of the other ticket. |
direction | string | One of: that_into_this, this_into_that · Default that_into_this |
ticket object.
400 | Same ticket / bad direction. |
404 | Ticket not found. |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/merge" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"target": "<target>"
}'const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/merge", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
"target": "<target>"
})
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/merge",
headers={"Authorization": "Bearer sk_live_…"},
json={
"target": "<target>",
},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/merge");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'target' => '<target>',
]),
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Retrieve a ticket
One ticket with its linked order, artwork, tags, thread, attachments and child tickets. Staff keys also receive participants, classification and privacy.
Required account permission: _Account_Ticket_View or _AdminConsole_Ticket_View
uuidrequired | string |
ticket object.
404 | Ticket not found (or not visible to the account) |
curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Update a ticket
Changes any combination of fields and relations — the equivalent of the admin console's title / summary / type / status / priority / classification / due date / privacy / assignment / customer / order / artwork / item / followers / tags actions in one call. Omitted fields are unchanged; send null to clear a relation. Customer keys may change title, summary, priority, status (Open / Closed), order, artwork and parent on their own tickets (published Articles are read-only to them: 403). Article summary updates extract inline images exactly as on create. Participants are emailed with the ticket-updated notification when type, status, priority, classification, due date or privacy change.
Required account permission: _Account_Ticket_Edit or _AdminConsole_Ticket_Edit
uuidrequired | string |
| Field | Type | Description |
|---|---|---|
title | string | Required on create (≤ 255). |
summary | string | Body HTML (≤ 256 KB after processing); null clears. Support tickets: <script>/<style> blocks and inline event handlers are stripped. Articles (type: 1): the full page is kept as sent — inline <style>, <script> and <link> are allowed and the knowledgebase renders such pages in a sandboxed frame; <img src="data:image/..."> payloads are extracted to the public bucket (knowledgebase/<ticket uuid>/<sha1>.<ext>), attached as files[] and the src rewritten to the public URL, so the 256 KB limit applies to the markup only. Relative src/href values (hero.png, styles.css) resolve against the Article's attached files. |
type | integer | See type_text. Customer keys: 2 Bug, 3 Complaint, 5 Discussion (default), 6 Feature Request, 12 Task. |
status | integer | See status_text. Customer keys: 9 Open (default) or 2 Closed. Staff keys: any; 11 Published makes an Article public on the knowledgebase when tagged knowledgebase or faq. |
priority | integer | 1 High, 2 Medium, 3 Low, 4 Shelved, 127 None (default). |
classification | integer | Staff keys. 1 Top Secret … 127 Unclassified (default). Cannot exceed the key owner's clearance. |
is_private | boolean | Staff keys. Private tickets are visible only to participants. |
due_at | string | Staff keys. ISO 8601; without an offset it is read in the key user's timezone. null clears. |
is_all_day | boolean | Staff keys. Whole-day deadline. |
order | string | Order uuid to link (null unlinks). Customer keys: must be the account's own order. |
artwork | string | Artwork uuid to link (null unlinks). Customer keys: must be the account's own artwork. |
item | string | Staff keys. Warehouse item uuid (null unlinks). |
parent | string | Parent ticket uuid (null clears). Must be visible to the account. |
customer | string | Staff keys. Customer account user uuid or sign-in email address (null clears). Defaults from the linked order / artwork. |
assignee | string | Staff keys. User uuid or sign-in email address (null unassigns). The key user is recorded as delegator. For set / unset without touching other fields see POST / DELETE /v1/tickets/:uuid/assignee. |
project_manager | string | Staff keys. User uuid or sign-in email address (null clears). |
followers | array of string | Staff keys. User uuids or sign-in email addresses; REPLACES the follower list (send the full list). To add or remove one follower without resending the list use POST / DELETE /v1/tickets/:uuid/followers. |
tags | array of string | Staff keys. Tag names; replaces the tag list, creating tags that do not exist yet (e.g. ["knowledgebase", "getting-started"]). |
ticket object.
400 | Validation failed. |
403 | Field not allowed for this key's account type, or classification above clearance. |
404 | Ticket not found. |
curl -X PATCH "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "PATCH",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.patch(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Delete a ticket
Permanently deletes the ticket and its calendar events (replies and attachments stay in storage, as in the admin console). Customer keys may delete only tickets their account created.
Required account permission: _Account_Ticket_Delete or _AdminConsole_Ticket_Delete
uuidrequired | string |
{deleted: true, uuid}
403 | Customer key deleting a ticket its account did not create. |
404 | Ticket not found. |
curl -X DELETE "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60", {
method: "DELETE",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.delete(
"https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Create a ticket
Opens a ticket exactly as Admin → Support → Tickets → New does (staff keys) or as a customer raising a support request (customer keys — the ticket is filed against the key's account as Open and Unclassified). Staff keys create knowledgebase content by posting type: 1 (Article), status: 11 (Published) and tags: ["knowledgebase"] or ["faq"]; the response carries knowledgebase_url. Every Article also carries knowledgebase_preview_url (knowledgebase/<slug>/?preview=<ticket uuid>), which renders the page as it will look once published — use it to proof a draft before flipping status to 11. Rich articles: send the complete HTML page in summary (inline <style>/<script> allowed), embed images as data: URIs (extracted to public storage automatically) or reference files by bare filename and attach them with POST /v1/tickets/:uuid/files — Article attachments are stored publicly under knowledgebase/<ticket uuid>/ and relative references resolve against them on the public page. Draft with status: 9 and PATCH to 11 once the files are attached. Participants are emailed with the ticket-created notification.
Required account permission: _Account_Ticket_Create or _AdminConsole_Ticket_Create
| Field | Type | Description |
|---|---|---|
title | string | Required on create (≤ 255). |
summary | string | Body HTML (≤ 256 KB after processing); null clears. Support tickets: <script>/<style> blocks and inline event handlers are stripped. Articles (type: 1): the full page is kept as sent — inline <style>, <script> and <link> are allowed and the knowledgebase renders such pages in a sandboxed frame; <img src="data:image/..."> payloads are extracted to the public bucket (knowledgebase/<ticket uuid>/<sha1>.<ext>), attached as files[] and the src rewritten to the public URL, so the 256 KB limit applies to the markup only. Relative src/href values (hero.png, styles.css) resolve against the Article's attached files. |
type | integer | See type_text. Customer keys: 2 Bug, 3 Complaint, 5 Discussion (default), 6 Feature Request, 12 Task. |
status | integer | See status_text. Customer keys: 9 Open (default) or 2 Closed. Staff keys: any; 11 Published makes an Article public on the knowledgebase when tagged knowledgebase or faq. |
priority | integer | 1 High, 2 Medium, 3 Low, 4 Shelved, 127 None (default). |
classification | integer | Staff keys. 1 Top Secret … 127 Unclassified (default). Cannot exceed the key owner's clearance. |
is_private | boolean | Staff keys. Private tickets are visible only to participants. |
due_at | string | Staff keys. ISO 8601; without an offset it is read in the key user's timezone. null clears. |
is_all_day | boolean | Staff keys. Whole-day deadline. |
order | string | Order uuid to link (null unlinks). Customer keys: must be the account's own order. |
artwork | string | Artwork uuid to link (null unlinks). Customer keys: must be the account's own artwork. |
item | string | Staff keys. Warehouse item uuid (null unlinks). |
parent | string | Parent ticket uuid (null clears). Must be visible to the account. |
customer | string | Staff keys. Customer account user uuid or sign-in email address (null clears). Defaults from the linked order / artwork. |
assignee | string | Staff keys. User uuid or sign-in email address (null unassigns). The key user is recorded as delegator. For set / unset without touching other fields see POST / DELETE /v1/tickets/:uuid/assignee. |
project_manager | string | Staff keys. User uuid or sign-in email address (null clears). |
followers | array of string | Staff keys. User uuids or sign-in email addresses; REPLACES the follower list (send the full list). To add or remove one follower without resending the list use POST / DELETE /v1/tickets/:uuid/followers. |
tags | array of string | Staff keys. Tag names; replaces the tag list, creating tags that do not exist yet (e.g. ["knowledgebase", "getting-started"]). |
ticket object.
400 | Validation failed — see errors[]. |
403 | Field not allowed for this key's account type, or classification above clearance. |
curl -X POST "https://api.heatapplied.com/v1/tickets" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets", {
method: "POST",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.post(
"https://api.heatapplied.com/v1/tickets",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]List tickets
Tickets visible to the account, newest first. A customer key sees the tickets raised for its account (never private or classified ones) plus, read-only, every published knowledgebase Article (type=1&tag=knowledgebase or tag=faq); a staff key sees tickets within its clearance, as in the admin console.
Required account permission: _Account_Ticket_List or _AdminConsole_Ticket_List
| Field | Type | Description |
|---|---|---|
page | integer | Default 1 |
per_page | integer | Max 100. Default 25 |
query | string | Matches title, summary or uuid. |
status | string | Comma-separated status codes, e.g. 9,1,7 for anything still being worked. |
type | string | Comma-separated type codes, e.g. 1 for knowledgebase articles. |
priority | string | Comma-separated priority codes. |
tag | string | Tag name — only tickets carrying it, e.g. faq. |
order | string | Order uuid — only tickets linked to that order. |
artwork | string | Artwork uuid — only tickets linked to that artwork. |
sort | string | One of: created_at, updated_at, due_at, status, priority, type, title · Default created_at |
order_by | string | Sort direction (order is taken by the order-uuid filter).One of: asc, desc · Default desc |
Paginated list of ticket objects in data.
curl "https://api.heatapplied.com/v1/tickets" \
-H "Authorization: Bearer sk_live_…"const response = await fetch("https://api.heatapplied.com/v1/tickets", {
method: "GET",
headers: { Authorization: "Bearer sk_live_…" }
});
const { data, errors } = await response.json();import requests
response = requests.get(
"https://api.heatapplied.com/v1/tickets",
headers={"Authorization": "Bearer sk_live_…"},
)
payload = response.json() # {"data": ..., "errors": [...]}$ch = curl_init("https://api.heatapplied.com/v1/tickets");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$payload = json_decode(curl_exec($ch), true); // ['data' => ..., 'errors' => [...]]Field reference for the objects returned and accepted above.
artwork| Field | Type | Description |
|---|---|---|
uuid | string | Identifier of the current version. Use it in every other artwork call and in order line items. Preflight completion creates a new version with a new uuid; any earlier uuid of the same artwork still resolves to the current version, and the response always carries the current uuid — store the one you get back. Example 3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60 |
name | string | Unique per account. Shown on invoices, packing slips and transfer labels. |
description | string | null | Free text notes. |
tin | string | null | Transfer Item Number — the SKU we manufacture this artwork under. Assigned once a compatible transfer size is resolved. |
type | string | Colour process. Spot = one screen per PMS colour; CMYK = 4-colour process; CMYK4Lights = process with white underbase for light garments. One of: CMYK, CMYK4Lights, Spot |
formula | string | Ink formula / transfer family the artwork is manufactured with. One of: Formula-1, Formula-2, Formula-4, Formula-5 |
status | integer | 1 Needs Reviewed, 2 Pending Preflight, 3 Preflight Approved, 4 Preflight Failed, 5 Preflight Required, 6 Pending Customer Approval. New uploads are 5; ordering a status-5 artwork submits it to preflight and the order waits in status 10 until it reaches 3. Status 4 cannot be ordered. One of: 1, 2, 3, 4, 5, 6 |
status_text | string | Human label for status. |
color_count | integer | Number of print colours (screens). |
colors | array of color | Print colours attached to the artwork. |
art_width | decimal | null | Detected artwork width in inches. |
art_height | decimal | null | Detected artwork height in inches. |
artboard_width | decimal | null | Artboard / requested print width in inches. |
artboard_height | decimal | null | Artboard / requested print height in inches. |
format | string | null | File extension of the stored original (pdf, ai, eps, png…). |
version | integer | Version number. Each edit creates a new version; the list endpoint only returns the latest. |
is_latest_version | boolean | |
is_archived | boolean | Archived artwork is hidden from the default listing and cannot be ordered. |
preview_png_url | string | null | Public PNG preview URL. |
preview_svg_url | string | null | Public SVG preview URL (vector artwork only). |
download_url | string | API URL that streams the current file — your upload until preflight completes, then the print-ready file (requires the same Bearer key). |
is_master | boolean | True for a master template (the artwork derivatives and Design Lab ideas are generated from). |
master | master | null | Master template details; null unless is_master. |
created_at | datetime | |
updated_at | datetime |
bank_account_request| Field | Type | Description |
|---|---|---|
payment_method_idrequired | string | The pm_… id from the confirmed SetupIntent (setup_intent.payment_method). |
labelrequired | string | Friendly name, e.g. "Operating account". |
billing_contactrequired | billing_contact |
billing_contact| Field | Type | Description |
|---|---|---|
namerequired | string | Cardholder / account holder name. |
street1required | string | |
street2 | string | |
street3 | string | |
cityrequired | string | |
staterequired | string | |
postal_coderequired | string | |
countryrequired | string | ISO 3166-1 alpha-2. Example US |
email_addressrequired | string | |
phone_number | string | |
mobile_number | string | |
attention | string | |
title | string |
bundle| Field | Type | Description |
|---|---|---|
uuid | string | Identifier of the sheet. Use it in every other bundle call. Example 7c2f1a9e-4b2d-4c3e-9a1f-1b2c3d4e5f60 |
name | string | Sheet name, shown in the library and on the sell sheet. |
sheet | string | Sheet size key. Example 28x17 |
dimensions | object | Physical and printable size of the sheet, in inches. |
status | string | saved is ready to order. ordered means it is on at least one order and can no longer be deleted. deleted sheets are hidden from the library.One of: draft, saved, ordered, deleted |
designs | array | The artwork on the sheet: artwork (uuid), name and number_up (how many times it is repeated). |
placements | array | Where each piece sits, in inches from the printable-area top-left, with rotated set when it was turned 90°. Returned only when the sheet has been arranged. |
overflow | array | Designs that did NOT fit on the sheet and were therefore not placed. Present on create/update responses only; an empty array means everything fit. |
preview_url | string | null | Sheet preview image. Null for sheets created through the API — the preview is produced by the web designer's canvas, so an API-created sheet has none until it is opened and saved there. |
created_at | string | ISO-8601 UTC. |
updated_at | string | ISO-8601 UTC. |
card_request| Field | Type | Description |
|---|---|---|
tokenrequired | string | Stripe PaymentMethod id (pm_…) or legacy token (tok_…) created client-side with the publishable key — never send raw card numbers. |
label | string | Friendly name. |
billing_contactrequired | billing_contact |
color| Field | Type | Description |
|---|---|---|
id | integer | Colour id. |
name | string | Colour name (e.g. "Royal Blue"). |
pms | string | null | Pantone reference (e.g. "286 C"). |
hex | string | null | Approximate sRGB hex (e.g. "#0033A0"). |
color_change| Field | Type | Description |
|---|---|---|
quantityrequired | integer | Transfers wanted in this colourway (≥ 1). Example 100 |
colorsrequired | array of string | One ink per swappable colour of the base artwork, **in the artwork's colors order** (underbase / adhesive screens are excluded and carried automatically). Each entry is a Pantone code ("185 C"), a colour name ("White"), or {id}. Keep an entry equal to the base colour to leave that position unchanged; at least one must differ.Example ["185 C","White"] |
name | string | Name for the colourway artwork that gets created (≤ 255). Defaults to "<artwork name> - <colours>". |
contact| Field | Type | Description |
|---|---|---|
name | string | Company or recipient name. |
attention | string | null | |
title | string | null | |
street1 | string | |
street2 | string | null | |
street3 | string | null | |
city | string | |
state | string | null | State / province code (e.g. VA). |
postal_code | string | |
country | string | ISO 3166-1 alpha-2 (e.g. US). |
email_address | string | null | |
phone_number | string | null | E.164. |
mobile_number | string | null | E.164. |
fax_number | string | null |
customer| Field | Type | Description |
|---|---|---|
uuid | string | Account root user uuid — the value to pass as customer on every act-as call. |
email | string | Sign-in email (also accepted as the customer act-as value). |
organization | string | |
status | integer | One of: 1, 2, 3, 4, 5 |
status_text | string | 1 Active, 2 Delinquent, 3 Inactive, 4 Pending Confirmation, 5 Lead. |
website | string | null | |
tax_exempt | boolean | |
payment_terms | integer | null | Net terms in days; null / 0 means pay at checkout. |
notes | string | null | Staff notes on the account (detail only). |
contact | contact | null | Default contact — name, address, phone, email. |
users | array | Detail only. [{uuid, email, status}] — the root sign-in plus every sub-user on the account. |
order_count | integer | |
artwork_count | integer | |
created_at | string | |
updated_at | string |
design_spec| Field | Type | Description |
|---|---|---|
typerequired | string | One of: CMYK, CMYK4Lights, Spot |
formularequired | string | One of: Formula-1, Formula-2, Formula-4, Formula-5 |
color_countrequired | integer | Number of spot colours (screens). Use 4 for CMYK / CMYK4Lights. |
widthrequired | decimal | Artwork width in inches. |
heightrequired | decimal | Artwork height in inches. |
color_ids | array of integer | Optional colour ids (see the colors on any artwork). Non-stock colours add a one-time custom ink match fee. |
font| Field | Type | Description |
|---|---|---|
id | integer | Stable platform font id. Example 368 |
font | string | Canonical {Family}-{Style} identity — the exact string the designer registers as a font-family and the compile request sends.Example Bebas Neue-Regular |
family | string | Font family name. Example Bebas Neue |
style | string | Style within the family. Example Regular |
file_name | string | Print file basename under the public S3 font/ prefix (always a static TrueType .ttf — the visibility gate requires it).Example Bebas Neue-Regular.ttf |
woff2_url | string | Absolute URL of the browser woff2 build. |
updated_at | string | UTC ISO 8601 last-modified stamp for this row. Example 2026-09-09T20:08:14Z |
invoice| Field | Type | Description |
|---|---|---|
uuid | string | |
id | string | Invoice number (I######). |
status | integer | One of: 1, 2, 3, 4, 5 |
status_text | string | 1 Unpaid, 2 Paid, 3 Void, 4 Pending Check, 5 Partially Paid. |
order | object | null | {uuid, id}. |
customer_purchase_order | string | null | |
subtotal_amount | integer | Cents. |
shipping_amount | integer | Cents. |
discount_amount | integer | Cents. |
tax_amount | integer | Cents. |
total_amount | integer | Cents. |
amount_paid | integer | Cents. Sum of captured / settled / initiated transactions. |
balance_due | integer | Cents. total_amount - amount_paid, floored at 0; always 0 on a void invoice. |
currency | string | All amounts on the invoice, its line items and transactions are integer minor units of this currency. Example USD |
bill_to | contact | |
line_items | array | [{description, quantity, unit_price, total_price, order_line_item_id}] (detail only). |
transactions | array of invoice_transaction | Detail only. |
download_url | string | PDF download (GET /v1/invoices/:uuid/download, Bearer auth). |
created_at | string | |
updated_at | string |
invoice_transaction| Field | Type | Description |
|---|---|---|
type | integer | 4 Check, 5 Card charge, 6 ACH, 7 Refund, 8 Account credit, 9 Account debit. |
status | integer | 1 Open, 2 Authorized, 3 Captured, 4 Settled, 5 Expired, 6 Refunded, 7 Void, 8 Reversed, 9 Initiated (ACH pending), 10 Failed. |
amount | integer | Cents. |
reference | string | null | Provider charge / intent id or check number. |
created_at | string |
line_item_request| Field | Type | Description |
|---|---|---|
artwork | object | Transfer artwork reference — {uuid} (preferred), {tin}, or {name}. A bare uuid string is also accepted. Omit for stock SKUs.Example {"uuid":"3f1c2a9e-\u2026"} |
sku | string | Stock SKU, or a specific transfer size SKU to pin (must be the size recommended for the artwork). |
quantityrequired | integer | Transfers in the artwork's own colours (the base colourway). Example 144 |
color_changes | array of color_change | Additional colourways of the same artwork, each {quantity, colors[], name?} — see color_change under Pricing. The line quantity becomes quantity + Σ color_changes[].quantity, priced at one break. Each colourway under 300 pcs bills service 366 once per changed ink. A colourway artwork is created (or an existing matching relative reused) and, once the order is placed, submitted for preflight; the order waits in status 10 until every colourway is approved. |
rush_days | integer | 0–3 production days to cut from the turn time. Billed as service 3 (unit fee × days). Ignored unless the account has rush enabled.Default 0 |
customer_sku | string | Your own SKU, echoed on the order and invoice. |
customer_sales_order | string | Your own sales order reference for this line. |
master| Field | Type | Description |
|---|---|---|
design_uuid | string | null | Uuid of the master Design row holding the schema-v1 document (null for an uploaded Illustrator-package master, which has no document — manage those in Admin → Artwork). |
scope | string | null | Design Lab gallery scope: global = every customer sees it as an idea (owned by the house stock account); customer = only scope_customer sees it (owned by that customer).One of: global, customer |
scope_customer | object | null | {uuid, name} of the customer a customer-scoped master belongs to. |
categories | array of string | Gallery category names. |
variables | array of master_variable | Text layers derivatives substitute, in document order. |
mutator_rules | array of mutator_rule | Ordered waterfall of variable mutators applied to every derivative data row before substitution. |
rotations | array of print_rotation | Authored press passes (one per separation, plus the HA ADHESIVE powder pass when inks touch). |
fonts | array of string | Font faces the document uses (<Family>-<Style>). |
derivative_count | integer | Current-version derivatives generated from this master. |
document_url | string | null | API URL returning the design document (staff keys). |
master_variable| Field | Type | Description |
|---|---|---|
key | string | Variable name — the data column derivatives bind to this layer. |
layer_id | string | Id of the text layer in the design document. |
default | string | null | Placeholder text the layer carries in the master (what the compiled PDF shows). |
mutator| Field | Type | Description |
|---|---|---|
name | string | Value to send as mutator_rules[].mutator. |
label | string | Human label. |
params | array of mutator_param | Parameters the mutator accepts (param keys). Empty when it takes none. |
mutator_param| Field | Type | Description |
|---|---|---|
key | string | Key inside param. |
label | string | |
type | string | number → integer; text → string; select → one of options.One of: number, text, select |
default | string | null | Value used when the key is omitted. |
options | array of string | Allowed values for select. |
mutator_rule| Field | Type | Description |
|---|---|---|
mutator | string | Registered mutator name — see GET /v1/artwork/mutators. |
variable_in | string | Variable the mutator reads (and, for most, the prefix of the fields it writes, e.g. Name → Name_Line1, Name_Line2). CreateField writes this key. |
param | object | null | Mutator parameters per its params schema (e.g. {"n": 1}); null when it takes none. |
sort_order | integer | Position in the waterfall (0-based). Rules run in this order. |
order| Field | Type | Description |
|---|---|---|
uuid | string | |
id | string | Human order number (S#####). |
status | integer | One of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 |
status_text | string | 1 Needs Mockups, 2 Needs Invoiced, 3 Pending Payment, 4 Unclaimed, 5 In Progress, 6 In Production, 7 Shipped, 8 Canceled, 9 Completed, 10 Pending Artwork, 11 Needs Reviewed. |
customer_purchase_order | string | null | |
notes | string | null | |
total_units | integer | |
subtotal_amount | integer | Cents. |
shipping_amount | integer | Cents. |
discount_amount | integer | Cents. |
tax_amount | integer | Cents. |
total_amount | integer | Cents. subtotal - discount + shipping + tax. |
currency | string | All amounts on the order and its line items are integer minor units of this currency. Example USD |
turn_days | integer | |
ship_date | string | null | Scheduled ship date (Y-m-d). |
delivery_date | string | null | |
shipping | object | {carrier, service, account_number} — account_number present only when shipping on your own account. |
ship_to | contact | |
bill_to | contact | |
line_items | array of order_line_item | |
shipments | array | Shipment summaries (see GET /v1/shipments). |
invoices | array | Invoice summaries (see GET /v1/invoices). |
print_spec_url | string | null | Mockup / print spec PDF. |
created_at | string | |
updated_at | string |
order_line_item| Field | Type | Description |
|---|---|---|
id | integer | |
description | string | |
sku | string | null | |
quantity | integer | Total transfers on the line across all colourways. |
turn_days | integer | |
unit_price | integer | Cents. Per-piece price excluding services. |
total_service_price | integer | Cents. Setup + colour upcharges + colour changes + rush for the line. |
total_price | integer | Cents. |
customer_sku | string | null | |
customer_sales_order | string | null | |
artwork | array | [{uuid, name, tin, status, status_text}] — the base artwork first, then one artwork per colour change. |
colorways | array | [{quantity, is_color_change, artwork:{uuid, name, status, status_text}, colors:[{id, name, pms, hex}]}] — one entry per colourway printed on this line (base first). Empty for stock SKUs. |
services | array | [{id, name, quantity, unit_price, total_price}] — 1 custom colour, 2 setup, 3 rush, 366 colour change (quantity = changed inks billed). |
order_request| Field | Type | Description |
|---|---|---|
customer_purchase_order | string | Your PO number (255 chars max). Echoed on the invoice. Example PO-10422 |
notes | string | Free-form production notes. |
ship_torequired | ship_to | |
line_itemsrequired | array of line_item_request | |
shippingrequired | shipping_request | |
payment | payment_request | Optional for accounts with payment terms (defaults to terms). |
pantone_color| Field | Type | Description |
|---|---|---|
id | integer | Stable platform color id — the same id color_ids accepts on pricing/order specs.Example 8612 |
name | string | Human-readable ink name. Example Red |
pms | string | Pantone Matching System code, or the ink name when the row has no PMS code — always populated, so this is the key to match on. Example 185 C |
hex | string | null | Web hex value, #RRGGBB.Example #E4002B |
rgb | object | null | {r, g, b}, each 0–255. null when the ink has no RGB values on file. |
cmyk | object | null | {c, m, y, k}, each 0–100. null when the ink has no CMYK values on file. |
is_special | boolean | Specialty ink (metallic, glitter, etc.). |
is_semiopaque | boolean | Semi-opaque ink — needs a base or special layering over dark substrates. |
updated_at | string | UTC ISO 8601 last-modified stamp for this row. Example 2026-09-01T14:03:22Z |
payment_method| Field | Type | Description |
|---|---|---|
uuid | string | Pass as payment.payment_method on POST /v1/orders. |
type | string | One of: card, bank_account |
label | string | null | |
is_default | boolean | |
brand | string | null | Cards: Visa, Mastercard, … |
last4 | string | null | |
exp_month | integer | null | Cards only. |
exp_year | integer | null | Cards only. |
bank_name | string | null | Bank accounts only. |
account_holder_name | string | null | Bank accounts only. |
routing_number | string | null | Bank accounts only. |
status | string | null | Bank accounts: new, pending (micro-deposits sent), verified. Unverified accounts cannot be charged. |
usable | boolean | True when the method can be charged by POST /v1/orders right now. |
billing_contact | contact | |
created_at | string |
payment_method_list| Field | Type | Description |
|---|---|---|
data | array of payment_method | |
terms_enabled | boolean | Account may pay by terms (payment.method: terms). |
check_enabled | boolean | Account may pay by check (payment.method: check). |
payment_terms_days | integer | Net terms in days when terms are enabled. |
payment_request| Field | Type | Description |
|---|---|---|
method | string | Defaults to terms when the account has payment terms, otherwise payment_method when a payment_method uuid is given.One of: payment_method, terms, check |
payment_method | string | UUID of a saved card or bank account from GET /v1/payment-methods. Cards are authorised and captured immediately; bank accounts (ACH) are initiated and settle asynchronously (invoice.paid webhook). |
confirmation_code | string | Check number / reference (method check only). |
print_file| Field | Type | Description |
|---|---|---|
id | integer | Production file id. |
artwork_uuid | string | Current uuid of the artwork the file belongs to. |
print_method_id | integer | Print method the file was prepared for (1 = Screen Printing Sheet-Fed). |
print_method | string | null | Print method name. |
version | integer | Per print-method version; each attach increments it and production always uses the highest. |
name | string | Display name (defaults to the artwork name). |
image_width | decimal | null | Trimmed printed-image width in inches, measured from the file. |
image_height | decimal | null | Trimmed printed-image height in inches. |
document_width | decimal | null | Page (document) width in inches. |
document_height | decimal | null | Page (document) height in inches. |
format | string | Always pdf. |
preview_png_url | string | null | Public low-res PNG preview of the production file. |
uploaded_by | object | null | {uuid, name} of the staff user whose key attached the file. |
created_at | datetime |
print_rotation| Field | Type | Description |
|---|---|---|
index | integer | Press pass number (1-based). |
color | color | null | Ink of the pass; null for the HA ADHESIVE powder pass. |
alternate_color_name | string | null | Named pass with no colour row (HA ADHESIVE). |
ink_type | string | Plastisol colour pass or Clear powder pass. |
adhesive_type | string | null | Powder adhesive applied on this pass (Dakota / 16P / 1P by formula); null when a dedicated powder pass carries it. |
screen_mesh_count | integer | Screen mesh. |
quote| Field | Type | Description |
|---|---|---|
artwork | object | null | {uuid, name, tin} when quoted from an artwork. |
spec | design_spec | The specification that was priced. |
matched | object | null | Transfer size the design was placed on: {sku, size_code, max_width, max_height, rotated, utilization_percent}. null when nothing fits — the design is too large for any size. |
quantity | integer | Base colourway quantity as requested. |
total_quantity | integer | quantity + Σ color_changes[].quantity — the quantity the design was priced at (all colourways share one price break). |
currency | string | Always USD. Every money field below is an integer in minor units (cents). |
unit_price | integer | Cents. All-in price per transfer at total_quantity: piece price + setup and one-time fees amortised. Excludes rush and colour-change fees. |
subtotal | integer | Cents. Transfers + setup + one-time fees (unit_price × total_quantity). |
breakdown | array | Itemised rows {label, quantity, unit_price, total_price} (cents) that sum to subtotal. |
color_changes | object | {unit_fee, minimum, colorways:[{quantity, colors:[color], changed, billable, fee}], total_fee} — fees in cents; changed is the number of ink positions that differ from the base; a colourway under minimum pieces bills unit_fee × changed. |
rush | object | {enabled, days, unit_fee, fee} — fees in cents; enabled tells you whether the account may request rush at all. |
total | integer | Cents. subtotal + color_changes.total_fee + rush.fee. Excludes shipping and tax. |
tiers | array | Standard quantity breaks {quantity, price_each, price_total} (cents) at 72, 144, 288, 512, 1024 for the same design. |
turn_days | integer | Business days to ship after rush is applied. |
estimated_ship_date | date | null | Ship date if ordered now (Y-m-d, business-day calendar). |
has_custom_pricing | boolean | True when negotiated account pricing was applied. |
quote_request| Field | Type | Description |
|---|---|---|
artwork | string | UUID of an artwork on your account. When given, spec is derived from it (and artwork-scoped pricing rules apply).Example 3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60 |
spec | design_spec | Design specification to quote when you do not have an artwork yet. Ignored when artwork is given. |
quantityrequired | integer | Number of transfers (≥ 1). Example 144 |
rush_days | integer | Days to cut from standard turn time, 0–3. Only honoured when the account has rush enabled (see rush.enabled in the response).Default 0 |
color_changes | array of color_change | Additional colourways of the same design (see color_change). quantity is the base colourway; the design is priced at quantity + Σ color_changes[].quantity. |
ship_to| Field | Type | Description |
|---|---|---|
namerequired | string | Example Acme Screen Printing |
attention | string | Example Receiving |
street1required | string | Example 123 Main St |
street2 | string | |
street3 | string | |
cityrequired | string | Example Richmond |
state | string | Required for US/CA destinations. Example VA |
postal_coderequired | string | Example 23220 |
countryrequired | string | ISO 3166-1 alpha-2. Example US |
email_addressrequired | string | Example receiving@acme.example |
phone_number | string | Required unless mobile_number is given.Example +18045551234 |
mobile_number | string | |
fax_number | string |
shipment| Field | Type | Description |
|---|---|---|
id | string | Shipment number (P######). Use in GET /v1/shipments/:id. |
status | integer | One of: 1, 2, 3, 4, 5, 6, 7 |
status_text | string | 1 Packaged, 2 Shipped, 3 Delivered, 4 Canceled, 5 Void, 6 Ready for Pickup, 7 Picked Up. |
carrier | string | null | Example UPS |
service | string | null | Example UPS GROUND |
account_number | string | null | Your carrier account when shipped third-party billed. |
tracking_numbers | array | Distinct tracking numbers across boxes. |
ship_to | contact | |
orders | array | [{uuid, id}] orders carried by this shipment. |
boxes | array of shipment_box | |
scheduled_pickup_date | string | null | |
pickup_date | string | null | |
scheduled_delivery_date | string | null | |
delivered_at | string | null | |
created_at | string | |
updated_at | string |
shipment_box| Field | Type | Description |
|---|---|---|
tracking_number | string | null | |
tracking_url | string | null | Carrier tracking page for the box. |
length | decimal | null | |
width | decimal | null | |
height | decimal | null | |
dimension_unit | string | null | Example IN |
weight | decimal | null | |
weight_unit | string | null | Example LBS |
line_items | array | [{order_line_item_id, quantity}] — what is packed in the box. |
shipping_options| Field | Type | Description |
|---|---|---|
carriers | array | Carriers accepted in shipping.carrier. |
services | object | Carrier → list of accepted shipping.service names. |
methods | array | shipping.method values this account may use (rate, plus account / pickup when enabled). |
live_rate_carriers | array | Carriers that support method: rate (currently UPS). |
shipping_rate| Field | Type | Description |
|---|---|---|
carrier | string | Example UPS |
service | string | Pass as shipping.service on the order.Example UPS GROUND |
service_code | string | Example 03 |
amount | integer | Cents (currency USD); what the order will bill for shipping. 0 for delayed-billing accounts. |
estimated_days | integer | Business days in transit after pickup. |
estimated_delivery_date | string | null | Y-m-d. |
shipping_rates| Field | Type | Description |
|---|---|---|
carrier | string | Example UPS |
pickup_date | date | Production ship date used for rating. |
turn_days | integer | |
subtotal_amount | integer | Cents. Merchandise subtotal used as declared value. |
currency | string | Always USD; all amounts are integer cents. |
rates | array of shipping_rate | Sorted cheapest first. |
shipping_rates_request| Field | Type | Description |
|---|---|---|
ship_torequired | ship_to | |
line_itemsrequired | array of line_item_request |
shipping_request| Field | Type | Description |
|---|---|---|
method | string | rate — bill a live UPS rate on the order (quote first with POST /v1/shipping/rates); account — ship on your own carrier account (account must have shipper numbers enabled); pickup — local pickup (account must have local pickup enabled).One of: rate, account, pickup · Default rate |
carrier | string | Carrier name. Only UPS supports rate.Default UPS |
service | string | Service name exactly as returned by GET /v1/shipping/options or POST /v1/shipping/rates (e.g. UPS GROUND). Required unless method is pickup.Example UPS GROUND |
account_number | string | Your carrier account number (required for account). |
ticket| Field | Type | Description |
|---|---|---|
uuid | string | |
id | string | Zero-padded ticket number shown in the admin console, e.g. 0057452. |
title | string | |
summary | string | null | Ticket body HTML. Listing returns it truncated to 500 characters. |
type | integer | One of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 |
type_text | string | 1 Article, 2 Bug, 3 Complaint, 4 Corrective Action, 5 Discussion, 6 Feature Request, 7 Investigation, 8 Machine Error, 9 Opportunity, 10 Project, 11 Resource, 12 Task. |
status | integer | One of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 |
status_text | string | 1 Active, 2 Closed, 3 Completed, 4 Draft, 5 Dropped, 6 Implemented, 7 Investigating, 8 On-Hold, 9 Open, 10 Planned, 11 Published, 12 Rejected, 13 Resolved, 14 Submitted, 15 Testing. |
priority | integer | One of: 1, 2, 3, 4, 127 |
priority_text | string | 1 High, 2 Medium, 3 Low, 4 Shelved, 127 None. |
classification | integer | null | Staff keys only. 1 Top Secret, 2 Secret, 3 Confidential, 126 Declassified, 127 Unclassified. |
classification_text | string | null | Staff keys only. |
is_private | boolean | Staff keys only; customer keys never see private tickets. |
due_at | string | null | Server-time Y-m-d H:i:s. |
is_all_day | boolean | Whether due_at is a whole-day deadline. |
knowledgebase_url | string | null | Public article URL when the ticket is a Published Article tagged knowledgebase or faq. |
knowledgebase_preview_url | string | null | For every Article (type: 1, any status / tags): knowledgebase/<slug>/?preview=<ticket uuid> renders the page exactly as it will look once published, with a "Preview" badge. Share only with people who may see the draft — the uuid is the credential. |
parent_ticket | object | null | {uuid, title} of the parent ticket. |
order | object | null | {uuid, id} of the linked order. |
artwork | object | null | {uuid, name, tin} of the linked artwork. |
item | object | null | Staff keys only. {uuid, name} of the linked warehouse item. |
tags | array of string | Tag names. |
customer | object | null | Staff keys only. {uuid, organization, name} of the affected customer account. |
assignee | object | null | {uuid, name} of the staff member working the ticket. |
project_manager | object | null | Staff keys only. {uuid, name}. |
delegator | object | null | Staff keys only. {uuid, name} of whoever assigned the ticket. |
creator | object | null | Staff keys only. {uuid, name}. |
followers | array of ticket_user | Staff keys, detail only. |
messages | array of ticket_message | Detail only. Chronological thread — customer keys see their own messages and staff replies sent to them. |
files | array of ticket_file | Detail only. Attachments on the ticket itself (customer keys: the account's own uploads). |
children | array | Detail only. [{uuid, title, status, status_text}] of child tickets visible to the account. |
created_at | string | |
updated_at | string |
ticket_file| Field | Type | Description |
|---|---|---|
uuid | string | |
name | string | |
mimetype | string | null | |
size | integer | null | Bytes. |
download | string | Authenticated download URL (GET /v1/tickets/:uuid/files/:fuuid/download). |
url | string | null | Public URL on the store domain for knowledgebase Article assets (/assets/knowledgebase/<ticket uuid>/<name>); null for private support-ticket attachments. |
created_at | string |
ticket_message| Field | Type | Description |
|---|---|---|
uuid | string | |
sender | object | null | {uuid, name} of the author (null for inbound email / system events). |
from | string | null | Email display name for inbound mail. |
subject | string | null | |
body | string | null | Message body HTML. |
is_system | boolean | True for automated thread events. |
files | array of ticket_file | Attachments on this reply. |
created_at | string |
ticket_tag| Field | Type | Description |
|---|---|---|
uuid | string | |
name | string |
ticket_user| Field | Type | Description |
|---|---|---|
uuid | string | |
name | string |
verify_request| Field | Type | Description |
|---|---|---|
amount1required | integer | First micro-deposit in cents (1–100), e.g. 32 for $0.32.Example 32 |
amount2required | integer | Second micro-deposit in cents (1–100). Example 45 |
Register endpoints under My Account → Settings → Webhooks, choosing which events to receive. Each delivery is an HTTPS POST with a JSON envelope; return any 2xx to acknowledge.
| Field | Type | Description |
|---|---|---|
id | string | Delivery UUID (unique per event delivery). |
type | string | Event type, e.g. order.created. |
created | integer | Unix timestamp the event was emitted. |
data | object | { "object": {...} } — the object payload for the event. |
X-Webhook-Event | Event type. |
X-Webhook-Delivery | Delivery UUID (matches body id). |
X-Webhook-Signature | t=<unix_ts>,v1=<hex HMAC-SHA256(secret, "<t>.<raw_body>")> |
User-Agent | EpicERP-Webhook/1.0 |
Compute HMAC-SHA256 over "<t>.<raw request body>" with the endpoint signing secret (shown once at creation) and compare in constant time with v1. Reject if |now - t| exceeds your tolerance (300s recommended).
// Node.js
const [t, v1] = sig.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now()/1000 - t) < 300;Any 2xx response marks the delivery Delivered; anything else (or a timeout) schedules a retry. Up to 6 attempts with back-off of 1, 5, 15, 60, 360, 1440 minutes.
| Group | Event | When |
|---|---|---|
| Orders | order.created | An order was placed (checkout or API). |
| Orders | order.status_changed | An order moved to a new status. |
| Orders | order.shipped | All shipments for an order have been created. |
| Orders | order.canceled | An order was canceled. |
| Shipments | shipment.created | A shipment (with carrier + tracking number) was created. |
| Shipments | shipment.tracking_updated | Carrier tracking status changed for a shipment. |
| Artwork | artwork.created | New artwork was uploaded to the library. |
| Artwork | artwork.preflight_passed | Preflight approved the artwork for production. |
| Artwork | artwork.preflight_failed | Preflight rejected the artwork; see reasons in data. |
| Artwork | artwork.approval_requested | Artwork is waiting on your approval (Pending Customer Approval). |
| Artwork | artwork.archived | Artwork was archived. |
| Artwork | proof.ready | A proof is ready for review/approval. |
| Invoices | invoice.created | An invoice was generated. |
| Invoices | invoice.paid | An invoice was paid in full. |
| Testing | test | Sent by the "Test" button in Settings → Webhooks. |