YOU ARE CONNECTED TO THE STAGING SERVER

Sign in / Register

Please provide email address.
Please enter a password.

Orders can be placed as soon as the quick online registration is complete.

Just a few benefits..
  • Fast online ordering and virtual proofs
  • View and pay invoices
  • View order history
  • View art library

API Reference v1

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 ·

Introduction

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.

Authentication

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.

Acting for a customer (staff keys)

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.

Errors

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).

typeMeaning
auth_errorMissing, unknown or expired API key.
security_errorHTTPS required, IP outside approved CIDR blocks, or request outside the key scope.
invalid_request_errorMissing/invalid parameter or resource not found.
rate_limit_errorToo many requests — honour the Retry-After header.
api_connection_errorUpstream connectivity failure.
api_errorUnexpected 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.

Money

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.

Pagination

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

Artwork — upload, list, retrieve, update, archive and download the print files you order transfers from.

GET/v1/artwork/mutators staff

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

Response

Paginated list of mutator objects in data.

Errors

403Key is not owned by a staff account.

Example request

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' => [...]]

GET/v1/artwork/:uuid/unarchive

Unarchive artwork

Restores an archived artwork to the active list.

Required account permission: _Account_Artwork_Archive or _AdminConsole_Artwork_Archive

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

artwork object.

Errors

404Artwork not found on this account.
409Not archived.

Example request

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' => [...]]

GET/v1/artwork/:uuid/archive

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

artwork object.

Errors

404Artwork not found on this account.
409Already archived.

Example request

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' => [...]]

GET/v1/artwork/:uuid/download

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

Binary file body with Content-Disposition: attachment.

Errors

403Download locked until first order.
404Artwork or file not found.

Example request

curl "https://api.heatapplied.com/v1/artwork/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
  -H "Authorization: Bearer sk_live_…" \
  -o artwork.pdf
import 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/v1/artwork/:uuid/document staff

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

Path parameters

uuidrequiredstring

Response

{artwork_uuid, design_uuid, document: {…}}

Errors

403Key is not owned by a staff account.
404Artwork not found.
409Not a master, or the master has no design document.

Example request

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' => [...]]

POST/v1/artwork/:uuid/production staff

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

Path parameters

uuidrequiredstring

Request body (multipart/form-data)

FieldTypeDescription
filerequiredfileThe production PDF (≤ 50 MB). Only .pdf is accepted.
print_method_idintegerPrint method the file is prepared for. 1 = Screen Printing Sheet-Fed (default).
Default 1
namestringDisplay name for the file (≤ 255). Defaults to the artwork name.

Response

print_file object.

Errors

400Validation failed, file not a PDF, or the PDF could not be rendered.
403Key is not owned by a staff account.
404Artwork or print method not found.

Example request

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' => [...]]

POST/v1/artwork/:uuid/derivative staff

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

Path parameters

uuidrequiredstring

Request body (multipart/form-data)

FieldTypeDescription
filerequiredfileThe finished derivative art file (≤ 50 MB). Only .pdf is accepted.
variablesrequiredstringJSON object of variable name to the value the file represents, e.g. {"City":"Dayton","State":"OH"}. Stored as the derivative's resolved values.
apply_mutatorsbooleanWhen 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
namestringDerivative name (≤ 255). Defaults to the master name with the variable values appended.
statusintegerStatus 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

Response

artwork object.

Errors

400Validation failed, variables missing or malformed, the target is not a master, or the PDF could not be rendered.
403Key is not owned by a staff account.
404Master artwork not found.

Example request

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' => [...]]

POST/v1/artwork/:uuid/package staff

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

Path parameters

uuidrequiredstring

Request body (multipart/form-data)

FieldTypeDescription
filerequiredfileThe Illustrator Package .zip (≤ 200 MB): exactly one master PDF outside Fonts/ / Links/.
formulastringInk formula for the new version. Defaults to the master's current formula.
One of: Formula-1, Formula-2, Formula-4, Formula-5
variablesstringJSON 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_rulesstringJSON 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.

Response

artwork object.

Errors

400Not 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.
403Key is not owned by a staff account.
404Artwork not found.
409Not a master, or the uuid is not the current head of its lineage.

Example request

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' => [...]]

PATCH/v1/artwork/:uuid

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
namestringNew unique name (≤ 255).
Example Team Logo — 2026 season
descriptionstringNew description (≤ 65535).
Example Front chest, 2-colour spot
statusintegerStaff 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
documentobjectStaff keys only, masters only. Replacement schema-v1 design document (object, or JSON string). Recompiles into a new artwork version.
variablesarrayStaff keys only, masters only. Replacement [{layer_id, key}] set (same rules as create).
mutator_rulesarrayStaff keys only, masters only. Replacement ordered rule list (same rules as create). [] clears all rules.
scopestringStaff 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
categoriesarray of stringStaff keys only, masters only. Replacement gallery category names (created when new). [] clears.
customerstringStaff 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

Response

artwork object.

Errors

400Validation / compile failed — see errors[].
403Customer key sent a staff-only field (status, document, …).
404Artwork not found on this account.
409Artwork 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).

Example request

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/v1/artwork/:uuid staff

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

Path parameters

uuidrequiredstring

Response

{deleted: 1, uuid, versions_deleted, derivatives_deleted, design_uuid}

Errors

403Key is not owned by a staff account.
404Artwork not found.
409Not 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.

Example request

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' => [...]]

GET/v1/artwork/:uuid

Retrieve artwork

Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

artwork object.

Errors

404Artwork not found on this account.

Example request

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' => [...]]

POST/v1/artwork

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

Request body (multipart/form-data)

FieldTypeDescription
filerequiredfileThe 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.
namerequiredstringUnique name for this artwork on the owning account (≤ 255; ≤ 128 for a master).
typerequiredstringColour process. Not used when master=1 (masters are always Spot, from the compiled separations).
One of: CMYK, CMYK4Lights, Spot
formularequiredstringInk formula. Optional for master=1 (default Formula-1).
One of: Formula-1, Formula-2, Formula-4, Formula-5
widthrequireddecimalRequested print width in inches (≤ 26.5). Not used when master=1 (artboard from the document).
heightrequireddecimalRequested print height in inches (≤ 19.25, or 19.5 when alternate size C is enabled on the owning account). Not used when master=1.
descriptionstringOptional notes (≤ 65535).
spot_colorarray of stringRequired for Spot. One entry per print colour: a colour name or Pantone code, e.g. spot_color[]=286 C. Not used when master=1.
preflightinteger1 to queue preflight now (status 2) rather than when first ordered. Ignored when a staff key sends status.
One of: 0, 1 · Default 0
customerstringStaff 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.
statusintegerStaff 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
masterintegerStaff 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
documentstringStaff 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.
variablesstringStaff 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_optoutintegerStaff 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_rulesstringStaff 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).
scopestringStaff keys only, with master=1. Gallery scope. Default customer when customer is sent, else global. customer requires customer.
One of: global, customer
categoriesstringStaff keys only, with master=1. JSON array of gallery category names (created when new), or a comma-separated list.

Response

artwork object.

Errors

400Validation 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).
403Customer key sent a staff-only field (customer, status, master, …).
404customer does not name a customer account.
409Artwork name already taken on the owning account.

Example request

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' => [...]]

GET/v1/artwork

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

Query parameters

FieldTypeDescription
pageinteger1-based page number.
Default 1
per_pageintegerRows per page, 1–100.
Default 24
querystringFree-text search across name, TIN, description and formula.
archivedbooleanWhen true, list archived artwork instead of active artwork.
Default
masterbooleanWhen 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
sortstringOne of: updated_at, created_at, name, tin · Default updated_at
orderstringOne of: asc, desc · Default desc
customerstringStaff 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

Response

Paginated list of artwork objects in data.

Example request

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' => [...]]

Bundles

Sheet bundles (sheet runs) — create, read, edit and delete a printable sheet made up of placed artwork.

GET/v1/bundles/sheets

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

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

{sheet, physical:{width,height}, printable:{width,height}, required_gap}[]

Example request

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' => [...]]

PATCH/v1/bundles/:uuid

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
designsarrayThe 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.
sheetstringSheet size key — see GET /v1/bundles/sheets.
Default 28x20
namestringSheet name.
Default Untitled Bundle
layoutobjectA 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.
scopestringStaff only. global builds the sheet on the house stock account, where global master artwork lives.
One of: global, customer
customerstringStaff only. Build on this customer account — uuid or sign-in email.

Response

bundle object.

Errors

400Same validation as create.
403scope or customer sent by a customer key.
404Bundle not found.
409The sheet is on an order, or has been deleted.

Example request

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/v1/bundles/:uuid

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

{deleted: 1, uuid}

Errors

404Bundle not found.
409On an order, mid-preflight, or already deleted.

Example request

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' => [...]]

GET/v1/bundles/:uuid

Retrieve a bundle

One sheet with its designs, arrangement and status.

Required account permission: _Account_Artwork_List or _AdminConsole_Artwork_List

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

bundle object.

Errors

404Bundle not found (or not on this account).

Example request

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' => [...]]

POST/v1/bundles

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

Request body (application/json)

FieldTypeDescription
designsarrayThe 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.
sheetstringSheet size key — see GET /v1/bundles/sheets.
Default 28x20
namestringSheet name.
Default Untitled Bundle
layoutobjectA 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.
scopestringStaff only. global builds the sheet on the house stock account, where global master artwork lives.
One of: global, customer
customerstringStaff only. Build on this customer account — uuid or sign-in email.

Response

bundle object.

Errors

400No 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).
403scope or customer sent by a customer key.

Example request

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' => [...]]

GET/v1/bundles

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 24
querystringMatches the sheet name.
statusstringOnly sheets in this state.
One of: draft, saved, ordered, deleted
sortstringOne of: updated_at, created_at, name · Default updated_at
orderstringOne of: asc, desc · Default desc
scopestringStaff only. global lists the house stock account's sheets.
One of: global, customer
customerstringStaff only. List this customer's sheets (uuid or email).

Response

Paginated list of bundle objects in data.

Example request

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

Colors endpoint — /v1/colors/pantone

GET/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)

Query parameters

FieldTypeDescription
updated_sincestringISO 8601 timestamp; only inks updated strictly after this moment are returned.
Example 2026-09-08T00:00:00Z

Response

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.

Errors

400Unparseable updated_since value.

Example request

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' => [...]]

Example response

{
  "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": []
}

Customers

/v1/customers — staff-only customer account directory: search / list, read, create, update.

PATCH/v1/customers/:uuid staff

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
emailstringNew sign-in email; must not belong to another user.
organizationstring
contactcontactAny subset of the contact fields.
statusintegerOne of: 1, 3, 5
websitestring | null
tax_exemptboolean
notesstring | null

Response

customer object.

Errors

400Malformed field or empty required value.
403Key is not a staff key or lacks _AdminConsole_Customer_Edit.
404No customer account matches.
409Email belongs to another user.

Example request

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' => [...]]

GET/v1/customers/:uuid staff

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

Path parameters

uuidrequiredstring

Response

customer object.

Errors

403Key is not a staff key or lacks _AdminConsole_Customer_List.
404No customer account matches.

Example request

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' => [...]]

POST/v1/customers staff

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

Request body (application/json)

FieldTypeDescription
emailrequiredstringSign-in email; unique across all users.
organizationrequiredstring
contactrequiredcontactname is required; address / phone fields optional. email_address defaults to the sign-in email.
statusinteger1 Active, 3 Inactive, 5 Lead.
One of: 1, 3, 5 · Default 1
websitestring
tax_exemptbooleanDefault
notesstring
send_emailbooleanEmail the customer a set-password link (Active accounts only).
Default

Response

customer object.

Errors

400Missing / malformed fields.
403Key is not a staff key or lacks _AdminConsole_Customer_Create.
409A non-lead user already exists with that email.

Example request

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' => [...]]

GET/v1/customers staff

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 24
querystringOrganization, email, contact name or phone number fragment.
statusstringComma-separated status codes (e.g. 1,2).
sortstringOne of: created_at, updated_at, organization, email, status · Default updated_at
orderstringOne of: asc, desc · Default desc

Response

Paginated list of customer objects in data.

Errors

403Key is not a staff key or lacks _AdminConsole_Customer_List.

Example request

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' => [...]]

Example response

{
  "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": []
}

Die-Cut Nesting Requests

API v1 endpoint for die-cut nesting requests.

POST/v1/die-cut-nesting/requests/:uuid/unarchive

Unarchive a nesting request

Required account permission: _Account_DieCutNesting_Edit

Path parameters

uuidrequiredstring

Example request

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' => [...]]

POST/v1/die-cut-nesting/requests/:uuid/archive

Archive a nesting request

Required account permission: _Account_DieCutNesting_Edit

Path parameters

uuidrequiredstring

Example request

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' => [...]]

GET/v1/die-cut-nesting/requests/:uuid/download

Download the nested output

Required account permission: _Account_DieCutNesting_Download

Path parameters

uuidrequiredstring

Response

Binary file body.

Example request

curl "https://api.heatapplied.com/v1/die-cut-nesting/requests/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
  -H "Authorization: Bearer sk_live_…" \
  -o artwork.pdf
import 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));

GET/v1/die-cut-nesting/requests/:uuid/files/:fuuid/download

Download an input file

Required account permission: _Account_DieCutNesting_Download

Path parameters

uuidrequiredstring
fuuidrequiredstring

Response

Binary file body.

Example request

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.pdf
import 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));

GET/v1/die-cut-nesting/requests/:uuid/files/:fuuid

Retrieve an input file

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring
fuuidrequiredstring

Example request

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' => [...]]

GET/v1/die-cut-nesting/requests/:uuid/files

List input files

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring

Example request

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' => [...]]

GET/v1/die-cut-nesting/requests/:uuid

Retrieve a nesting request

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring

Errors

404Request not found

Example request

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' => [...]]

POST/v1/die-cut-nesting/requests

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

Example request

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' => [...]]

GET/v1/die-cut-nesting/requests

List nesting requests

Paginated die-cut nesting jobs (page, per_page).

Required account permission: _Account_DieCutNesting_List

Example request

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' => [...]]

Die-Cut Nesting Templates

API endpoint for die-cut nesting template management (v1).

GET/v1/die-cut-nesting/templates/:uuid/download

Download the template file

Required account permission: _Account_DieCutNesting_Download

Path parameters

uuidrequiredstring

Response

Binary file body.

Example request

curl "https://api.heatapplied.com/v1/die-cut-nesting/templates/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
  -H "Authorization: Bearer sk_live_…" \
  -o artwork.pdf
import 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));

GET/v1/die-cut-nesting/templates/:uuid/files/:fuuid/download

Download a template file

Required account permission: _Account_DieCutNesting_Download

Path parameters

uuidrequiredstring
fuuidrequiredstring

Response

Binary file body.

Example request

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.pdf
import 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));

GET/v1/die-cut-nesting/templates/:uuid/files/:fuuid

Retrieve a template file

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring
fuuidrequiredstring

Example request

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/v1/die-cut-nesting/templates/:uuid/files/:fuuid

Delete a template file

Required account permission: _Account_DieCutNesting_Edit

Path parameters

uuidrequiredstring
fuuidrequiredstring

Example request

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' => [...]]

GET/v1/die-cut-nesting/templates/:uuid/files

List template files

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring

Example request

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/v1/die-cut-nesting/templates/:uuid

Delete a template

Required account permission: _Account_DieCutNesting_Delete

Path parameters

uuidrequiredstring

Errors

404Template not found

Example request

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' => [...]]

GET/v1/die-cut-nesting/templates/:uuid

Retrieve a template

Required account permission: _Account_DieCutNesting_List

Path parameters

uuidrequiredstring

Errors

404Template not found

Example request

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' => [...]]

PUT/v1/die-cut-nesting/templates/:uuid

Update a template

Required account permission: _Account_DieCutNesting_Edit

Path parameters

uuidrequiredstring

Errors

404Template not found

Example request

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' => [...]]

POST/v1/die-cut-nesting/templates

Create a template

Required account permission: _Account_DieCutNesting_Create

Example request

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' => [...]]

GET/v1/die-cut-nesting/templates

List templates

Paginated die-cut nesting templates (block positions used by imposition).

Required account permission: _Account_DieCutNesting_List

Example request

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

Fonts endpoint — /v1/fonts

GET/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)

Query parameters

FieldTypeDescription
updated_sincestringISO 8601 timestamp; only fonts updated strictly after this moment are returned.
Example 2026-09-08T00:00:00Z

Response

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.

Errors

400Unparseable updated_since value.

Example request

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' => [...]]

Example response

{
  "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": []
}

Invoices

API v1 Invoices — list, retrieve and download invoices.

GET/v1/invoices/:uuid/download

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

Binary PDF body with Content-Disposition: attachment.

Errors

404Invoice not found

Example request

curl "https://api.heatapplied.com/v1/invoices/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/download" \
  -H "Authorization: Bearer sk_live_…" \
  -o invoice.pdf
import 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));

GET/v1/invoices/:uuid

Retrieve an invoice

Required account permission: _Account_Invoice_View or _AdminConsole_Invoice_View

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

invoice object.

Errors

404Invoice not found

Example request

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' => [...]]

GET/v1/invoices

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 24
querystringMatches invoice number (I000123), order number or your PO number.
statusstringComma-separated status codes, e.g. 1,5 for anything with a balance.
orderstringOrder uuid — only invoices for that order.
sortstringOne of: created_at, updated_at, total_amount, full_id, status · Default created_at
order_bystringSort direction (order is taken by the order-uuid filter).
One of: asc, desc · Default desc
customerstringStaff 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

Response

Paginated list of invoice objects in data.

Example request

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' => [...]]

Orders

API v1 Orders — list, retrieve and create sales orders.

GET/v1/orders/:uuid/shipments

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

Paginated list of shipment objects in data.

Errors

404Order not found

Example request

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' => [...]]

GET/v1/orders/:uuid/invoices

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

Paginated list of invoice objects in data.

Errors

404Order not found

Example request

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' => [...]]

GET/v1/orders/:uuid

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

Path parameters

uuidrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

order object.

Errors

404Order not found

Example request

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' => [...]]

POST/v1/orders

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

Request body (application/json)

FieldTypeDescription
customer_purchase_orderstringYour PO number (255 chars max). Echoed on the invoice.
Example PO-10422
notesstringFree-form production notes.
ship_torequiredship_to
line_itemsrequiredarray of line_item_request
shippingrequiredshipping_request
paymentpayment_requestOptional for accounts with payment terms (defaults to terms).
customerstringStaff 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

Response

order object.

Errors

400Validation failure — errors[0].message names the offending field (e.g. line_items[0].artwork.uuid '…' not found).
402Payment declined / could not be authorised.

Example request

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' => [...]]

GET/v1/orders

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 24
querystringMatches order number (S01234) or your PO number.
statusstringComma-separated status codes, e.g. 6,7.
created_afterstringY-m-d inclusive.
created_beforestringY-m-d inclusive.
sortstringOne of: created_at, updated_at, ship_date, delivery_date, total_amount, full_id, status · Default created_at
orderstringOne of: asc, desc · Default desc
customerstringStaff 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

Response

Paginated list of order objects in data.

Example request

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' => [...]]

Payment Methods

API v1 Payment Methods — saved cards and bank accounts used by POST /v1/orders.

GET/v1/payment-methods/cards/setup

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

Response

Object {provider: \"stripe\", publishable_key, mode}.

Errors

400No card-capable payment provider is configured for the sales channel.

Example request

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' => [...]]

POST/v1/payment-methods/banks/setup

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

Response

Object {client_secret}.

Example request

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' => [...]]

POST/v1/payment-methods/banks

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

Request body (application/json)

FieldTypeDescription
payment_method_idrequiredstringThe pm_… id from the confirmed SetupIntent (setup_intent.payment_method).
labelrequiredstringFriendly name, e.g. "Operating account".
billing_contactrequiredbilling_contact

Response

payment_method object.

Errors

400Missing field, limit reached, or the provider rejected the payment method.

Example request

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' => [...]]

POST/v1/payment-methods/cards

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

Request body (application/json)

FieldTypeDescription
tokenrequiredstringStripe PaymentMethod id (pm_…) or legacy token (tok_…) created client-side with the publishable key — never send raw card numbers.
labelstringFriendly name.
billing_contactrequiredbilling_contact

Response

payment_method object.

Errors

400Missing field, limit reached, or the provider rejected the token (errors[0].message).

Example request

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' => [...]]

POST/v1/payment-methods/:uuid/verify

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
amount1requiredintegerFirst micro-deposit in cents (1–100), e.g. 32 for $0.32.
Example 32
amount2requiredintegerSecond micro-deposit in cents (1–100).
Example 45

Response

payment_method object.

Errors

400Amounts not integer cents, out of range (1–100) or do not match.
404Payment method not found

Example request

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' => [...]]

DELETE/v1/payment-methods/:uuid

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

Path parameters

uuidrequiredstring

Response

Object {deleted: true}.

Errors

404Payment method not found

Example request

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' => [...]]

GET/v1/payment-methods

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

Response

payment_method_list object.

Example request

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

Pricing — quote heat-transfer prices before you order.

POST/v1/pricing/quote

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

Request body (application/json)

FieldTypeDescription
artworkstringArtwork UUID (alternative to spec).
specdesign_specDesign specification (alternative to artwork).
quantityrequiredintegerBase colourway quantity.
rush_daysinteger0–3.
Default 0
color_changesarray of color_changeAdditional colourways (see color_change).
customerstringStaff 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

Response

quote object.

Errors

400Missing/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.
404Artwork not found on this account.

Example request

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' => [...]]

Example response

{
  "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": []
}

Shipments

API v1 Shipments — read-only access to outbound shipments, their boxes and tracking.

GET/v1/shipments/:id

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

Path parameters

idrequiredstring

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

shipment object.

Errors

404Shipment not found

Example request

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' => [...]]

GET/v1/shipments

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 24
statusstringComma-separated status codes.
orderstringOrder uuid — only shipments for that order.
tracking_numberstringFind the shipment containing a tracking number.
customerstringStaff 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

Response

Paginated list of shipment objects in data.

Example request

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' => [...]]

Shipping

API v1 Shipping — carrier options and live UPS rate quotes ahead of POST /v1/orders.

GET/v1/shipping/options

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

Query parameters

FieldTypeDescription
customerstringStaff 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

Response

shipping_options object.

Example request

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' => [...]]

POST/v1/shipping/rates

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

Request body (application/json)

FieldTypeDescription
ship_torequiredship_to
line_itemsrequiredarray of line_item_request
customerstringStaff 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

Response

shipping_rates object.

Errors

400Validation failure or carrier rating error (errors[0].message).

Example request

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' => [...]]

Status

Ping endpoint — /v1/ping

GET/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)

Response

Object {timestamp}.

Example request

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' => [...]]

Team Events

API endpoint for team event management (v1).

DELETE/v1/teams/events/:uuid

Delete a team event

Required account permission: _Account_Team_Delete

Path parameters

uuidrequiredstring

Errors

404Event not found

Example request

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' => [...]]

GET/v1/teams/events/:uuid

Retrieve a team event

Required account permission: _Account_Team_List

Path parameters

uuidrequiredstring

Errors

404Event not found

Example request

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' => [...]]

PUT/v1/teams/events/:uuid

Update a team event

Required account permission: _Account_Team_Edit

Path parameters

uuidrequiredstring

Errors

404Event not found

Example request

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' => [...]]

POST/v1/teams/events

Create a team event

Required account permission: _Account_Team_Create

Example request

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' => [...]]

GET/v1/teams/events

List team events

Paginated team events (games, tournaments, fundraisers) for the account (page, per_page).

Required account permission: _Account_Team_List

Example request

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' => [...]]

Team Rosters

API endpoint for team roster management (v1).

DELETE/v1/teams/rosters/:uuid

Delete a roster

Required account permission: _Account_Team_Delete

Path parameters

uuidrequiredstring

Errors

404Roster not found

Example request

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' => [...]]

GET/v1/teams/rosters/:uuid

Retrieve a roster

Required account permission: _Account_Team_List

Path parameters

uuidrequiredstring

Errors

404Roster not found

Example request

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' => [...]]

PUT/v1/teams/rosters/:uuid

Update a roster

Required account permission: _Account_Team_Edit

Path parameters

uuidrequiredstring

Errors

404Roster not found

Example request

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' => [...]]

POST/v1/teams/rosters

Create a roster

Creates a roster of members (name, number, sizes) attached to a team event.

Required account permission: _Account_Team_Create

Example request

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' => [...]]

GET/v1/teams/rosters

List rosters

Paginated rosters for the account (page, per_page).

Required account permission: _Account_Team_List

Example request

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' => [...]]

Tickets

API v1 Tickets — full create / read / update / delete plus the ticket actions of Admin → Support → Tickets (reply, attachments, tags, followers, assignment, merge).

GET/v1/tickets/options

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

Response

{types, statuses, priorities, classifications?, customer_types, customer_statuses}

Example request

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' => [...]]

GET/v1/tickets/tags

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

Response

Paginated list of ticket_tag objects in data.

Example request

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' => [...]]

POST/v1/tickets/tags staff

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

Request body (application/json)

FieldTypeDescription
namerequiredstring≤ 64 characters.

Response

ticket_tag object.

Errors

400Missing or too-long name.

Example request

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' => [...]]

GET/v1/tickets/:uuid/files/:fuuid/download

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

Path parameters

uuidrequiredstring
fuuidrequiredstring

Response

Binary file body with Content-Disposition: attachment.

Errors

404Ticket or file not found.

Example request

curl "https://api.heatapplied.com/v1/tickets/3f1c2a9e-8b2d-4c3e-9a1f-1b2c3d4e5f60/files/{fuuid}/download" \
  -H "Authorization: Bearer sk_live_…" \
  -o artwork.pdf
import 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));

DELETE/v1/tickets/:uuid/files/:fuuid

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

Path parameters

uuidrequiredstring
fuuidrequiredstring

Response

ticket object.

Errors

404Ticket or file not found.

Example request

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' => [...]]

POST/v1/tickets/:uuid/files

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

Path parameters

uuidrequiredstring

Request body (multipart/form-data)

FieldTypeDescription
filerequiredfileOne or more files (≤ 50 MB each).
messagestringReply uuid to attach to instead of the ticket.

Response

ticket object.

Errors

400No file / file too large.
404Ticket or message not found.

Example request

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' => [...]]

PATCH/v1/tickets/:uuid/messages/:muuid

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

Path parameters

uuidrequiredstring
muuidrequiredstring

Request body (application/json)

FieldTypeDescription
bodyrequiredstring

Response

ticket object.

Errors

403Not the author (customer keys).
404Ticket or message not found.

Example request

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/v1/tickets/:uuid/messages/:muuid

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

Path parameters

uuidrequiredstring
muuidrequiredstring

Response

ticket object.

Errors

403Not the author (customer keys).
404Ticket or message not found.

Example request

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' => [...]]

POST/v1/tickets/:uuid/messages

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
bodyrequiredstringReply HTML (≤ 256 KB).

Response

ticket object.

Errors

400Empty body.
404Ticket not found.

Example request

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' => [...]]

DELETE/v1/tickets/:uuid/followers/:user staff

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

Path parameters

uuidrequiredstring
userrequiredstring

Response

ticket object.

Errors

403Customer key.
404Ticket not found, user not found, or user is not following this ticket.

Example request

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' => [...]]

POST/v1/tickets/:uuid/followers staff

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
usersrequiredarray of stringUser uuids or sign-in email addresses to add. A single string is accepted too.

Response

ticket object.

Errors

400No users given, or a user was not found.
403Customer key.
404Ticket not found.

Example request

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' => [...]]

DELETE/v1/tickets/:uuid/assignee staff

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

Path parameters

uuidrequiredstring

Response

ticket object.

Errors

403Customer key.
404Ticket not found.

Example request

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' => [...]]

POST/v1/tickets/:uuid/assignee staff

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
userrequiredstringUser uuid or sign-in email address.

Response

ticket object.

Errors

400No user given, or user not found.
403Customer key.
404Ticket not found.

Example request

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' => [...]]

POST/v1/tickets/:uuid/merge staff

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
targetrequiredstringUuid of the other ticket.
directionstringOne of: that_into_this, this_into_that · Default that_into_this

Response

ticket object.

Errors

400Same ticket / bad direction.
404Ticket not found.

Example request

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' => [...]]

GET/v1/tickets/:uuid

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

Path parameters

uuidrequiredstring

Response

ticket object.

Errors

404Ticket not found (or not visible to the account)

Example request

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' => [...]]

PATCH/v1/tickets/:uuid

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

Path parameters

uuidrequiredstring

Request body (application/json)

FieldTypeDescription
titlestringRequired on create (≤ 255).
summarystringBody 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.
typeintegerSee type_text. Customer keys: 2 Bug, 3 Complaint, 5 Discussion (default), 6 Feature Request, 12 Task.
statusintegerSee 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.
priorityinteger1 High, 2 Medium, 3 Low, 4 Shelved, 127 None (default).
classificationintegerStaff keys. 1 Top Secret … 127 Unclassified (default). Cannot exceed the key owner's clearance.
is_privatebooleanStaff keys. Private tickets are visible only to participants.
due_atstringStaff keys. ISO 8601; without an offset it is read in the key user's timezone. null clears.
is_all_daybooleanStaff keys. Whole-day deadline.
orderstringOrder uuid to link (null unlinks). Customer keys: must be the account's own order.
artworkstringArtwork uuid to link (null unlinks). Customer keys: must be the account's own artwork.
itemstringStaff keys. Warehouse item uuid (null unlinks).
parentstringParent ticket uuid (null clears). Must be visible to the account.
customerstringStaff keys. Customer account user uuid or sign-in email address (null clears). Defaults from the linked order / artwork.
assigneestringStaff 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_managerstringStaff keys. User uuid or sign-in email address (null clears).
followersarray of stringStaff 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.
tagsarray of stringStaff keys. Tag names; replaces the tag list, creating tags that do not exist yet (e.g. ["knowledgebase", "getting-started"]).

Response

ticket object.

Errors

400Validation failed.
403Field not allowed for this key's account type, or classification above clearance.
404Ticket not found.

Example request

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/v1/tickets/:uuid

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

Path parameters

uuidrequiredstring

Response

{deleted: true, uuid}

Errors

403Customer key deleting a ticket its account did not create.
404Ticket not found.

Example request

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' => [...]]

POST/v1/tickets

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

Request body (application/json)

FieldTypeDescription
titlestringRequired on create (≤ 255).
summarystringBody 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.
typeintegerSee type_text. Customer keys: 2 Bug, 3 Complaint, 5 Discussion (default), 6 Feature Request, 12 Task.
statusintegerSee 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.
priorityinteger1 High, 2 Medium, 3 Low, 4 Shelved, 127 None (default).
classificationintegerStaff keys. 1 Top Secret … 127 Unclassified (default). Cannot exceed the key owner's clearance.
is_privatebooleanStaff keys. Private tickets are visible only to participants.
due_atstringStaff keys. ISO 8601; without an offset it is read in the key user's timezone. null clears.
is_all_daybooleanStaff keys. Whole-day deadline.
orderstringOrder uuid to link (null unlinks). Customer keys: must be the account's own order.
artworkstringArtwork uuid to link (null unlinks). Customer keys: must be the account's own artwork.
itemstringStaff keys. Warehouse item uuid (null unlinks).
parentstringParent ticket uuid (null clears). Must be visible to the account.
customerstringStaff keys. Customer account user uuid or sign-in email address (null clears). Defaults from the linked order / artwork.
assigneestringStaff 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_managerstringStaff keys. User uuid or sign-in email address (null clears).
followersarray of stringStaff 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.
tagsarray of stringStaff keys. Tag names; replaces the tag list, creating tags that do not exist yet (e.g. ["knowledgebase", "getting-started"]).

Response

ticket object.

Errors

400Validation failed — see errors[].
403Field not allowed for this key's account type, or classification above clearance.

Example request

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' => [...]]

GET/v1/tickets

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

Query parameters

FieldTypeDescription
pageintegerDefault 1
per_pageintegerMax 100.
Default 25
querystringMatches title, summary or uuid.
statusstringComma-separated status codes, e.g. 9,1,7 for anything still being worked.
typestringComma-separated type codes, e.g. 1 for knowledgebase articles.
prioritystringComma-separated priority codes.
tagstringTag name — only tickets carrying it, e.g. faq.
orderstringOrder uuid — only tickets linked to that order.
artworkstringArtwork uuid — only tickets linked to that artwork.
sortstringOne of: created_at, updated_at, due_at, status, priority, type, title · Default created_at
order_bystringSort direction (order is taken by the order-uuid filter).
One of: asc, desc · Default desc

Response

Paginated list of ticket objects in data.

Example request

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' => [...]]

Objects

Field reference for the objects returned and accepted above.

artwork

FieldTypeDescription
uuidstringIdentifier 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
namestringUnique per account. Shown on invoices, packing slips and transfer labels.
descriptionstring | nullFree text notes.
tinstring | nullTransfer Item Number — the SKU we manufacture this artwork under. Assigned once a compatible transfer size is resolved.
typestringColour process. Spot = one screen per PMS colour; CMYK = 4-colour process; CMYK4Lights = process with white underbase for light garments.
One of: CMYK, CMYK4Lights, Spot
formulastringInk formula / transfer family the artwork is manufactured with.
One of: Formula-1, Formula-2, Formula-4, Formula-5
statusinteger1 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_textstringHuman label for status.
color_countintegerNumber of print colours (screens).
colorsarray of colorPrint colours attached to the artwork.
art_widthdecimal | nullDetected artwork width in inches.
art_heightdecimal | nullDetected artwork height in inches.
artboard_widthdecimal | nullArtboard / requested print width in inches.
artboard_heightdecimal | nullArtboard / requested print height in inches.
formatstring | nullFile extension of the stored original (pdf, ai, eps, png…).
versionintegerVersion number. Each edit creates a new version; the list endpoint only returns the latest.
is_latest_versionboolean
is_archivedbooleanArchived artwork is hidden from the default listing and cannot be ordered.
preview_png_urlstring | nullPublic PNG preview URL.
preview_svg_urlstring | nullPublic SVG preview URL (vector artwork only).
download_urlstringAPI URL that streams the current file — your upload until preflight completes, then the print-ready file (requires the same Bearer key).
is_masterbooleanTrue for a master template (the artwork derivatives and Design Lab ideas are generated from).
mastermaster | nullMaster template details; null unless is_master.
created_atdatetime
updated_atdatetime

bank_account_request

FieldTypeDescription
payment_method_idrequiredstringThe pm_… id from the confirmed SetupIntent (setup_intent.payment_method).
labelrequiredstringFriendly name, e.g. "Operating account".
billing_contactrequiredbilling_contact

billing_contact

FieldTypeDescription
namerequiredstringCardholder / account holder name.
street1requiredstring
street2string
street3string
cityrequiredstring
staterequiredstring
postal_coderequiredstring
countryrequiredstringISO 3166-1 alpha-2.
Example US
email_addressrequiredstring
phone_numberstring
mobile_numberstring
attentionstring
titlestring

bundle

FieldTypeDescription
uuidstringIdentifier of the sheet. Use it in every other bundle call.
Example 7c2f1a9e-4b2d-4c3e-9a1f-1b2c3d4e5f60
namestringSheet name, shown in the library and on the sell sheet.
sheetstringSheet size key.
Example 28x17
dimensionsobjectPhysical and printable size of the sheet, in inches.
statusstringsaved 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
designsarrayThe artwork on the sheet: artwork (uuid), name and number_up (how many times it is repeated).
placementsarrayWhere 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.
overflowarrayDesigns 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_urlstring | nullSheet 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_atstringISO-8601 UTC.
updated_atstringISO-8601 UTC.

card_request

FieldTypeDescription
tokenrequiredstringStripe PaymentMethod id (pm_…) or legacy token (tok_…) created client-side with the publishable key — never send raw card numbers.
labelstringFriendly name.
billing_contactrequiredbilling_contact

color

FieldTypeDescription
idintegerColour id.
namestringColour name (e.g. "Royal Blue").
pmsstring | nullPantone reference (e.g. "286 C").
hexstring | nullApproximate sRGB hex (e.g. "#0033A0").

color_change

FieldTypeDescription
quantityrequiredintegerTransfers wanted in this colourway (≥ 1).
Example 100
colorsrequiredarray of stringOne 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"]
namestringName for the colourway artwork that gets created (≤ 255). Defaults to "<artwork name> - <colours>".

contact

FieldTypeDescription
namestringCompany or recipient name.
attentionstring | null
titlestring | null
street1string
street2string | null
street3string | null
citystring
statestring | nullState / province code (e.g. VA).
postal_codestring
countrystringISO 3166-1 alpha-2 (e.g. US).
email_addressstring | null
phone_numberstring | nullE.164.
mobile_numberstring | nullE.164.
fax_numberstring | null

customer

FieldTypeDescription
uuidstringAccount root user uuid — the value to pass as customer on every act-as call.
emailstringSign-in email (also accepted as the customer act-as value).
organizationstring
statusintegerOne of: 1, 2, 3, 4, 5
status_textstring1 Active, 2 Delinquent, 3 Inactive, 4 Pending Confirmation, 5 Lead.
websitestring | null
tax_exemptboolean
payment_termsinteger | nullNet terms in days; null / 0 means pay at checkout.
notesstring | nullStaff notes on the account (detail only).
contactcontact | nullDefault contact — name, address, phone, email.
usersarrayDetail only. [{uuid, email, status}] — the root sign-in plus every sub-user on the account.
order_countinteger
artwork_countinteger
created_atstring
updated_atstring

design_spec

FieldTypeDescription
typerequiredstringOne of: CMYK, CMYK4Lights, Spot
formularequiredstringOne of: Formula-1, Formula-2, Formula-4, Formula-5
color_countrequiredintegerNumber of spot colours (screens). Use 4 for CMYK / CMYK4Lights.
widthrequireddecimalArtwork width in inches.
heightrequireddecimalArtwork height in inches.
color_idsarray of integerOptional colour ids (see the colors on any artwork). Non-stock colours add a one-time custom ink match fee.

font

FieldTypeDescription
idintegerStable platform font id.
Example 368
fontstringCanonical {Family}-{Style} identity — the exact string the designer registers as a font-family and the compile request sends.
Example Bebas Neue-Regular
familystringFont family name.
Example Bebas Neue
stylestringStyle within the family.
Example Regular
file_namestringPrint file basename under the public S3 font/ prefix (always a static TrueType .ttf — the visibility gate requires it).
Example Bebas Neue-Regular.ttf
woff2_urlstringAbsolute URL of the browser woff2 build.
updated_atstringUTC ISO 8601 last-modified stamp for this row.
Example 2026-09-09T20:08:14Z

invoice

FieldTypeDescription
uuidstring
idstringInvoice number (I######).
statusintegerOne of: 1, 2, 3, 4, 5
status_textstring1 Unpaid, 2 Paid, 3 Void, 4 Pending Check, 5 Partially Paid.
orderobject | null{uuid, id}.
customer_purchase_orderstring | null
subtotal_amountintegerCents.
shipping_amountintegerCents.
discount_amountintegerCents.
tax_amountintegerCents.
total_amountintegerCents.
amount_paidintegerCents. Sum of captured / settled / initiated transactions.
balance_dueintegerCents. total_amount - amount_paid, floored at 0; always 0 on a void invoice.
currencystringAll amounts on the invoice, its line items and transactions are integer minor units of this currency.
Example USD
bill_tocontact
line_itemsarray[{description, quantity, unit_price, total_price, order_line_item_id}] (detail only).
transactionsarray of invoice_transactionDetail only.
download_urlstringPDF download (GET /v1/invoices/:uuid/download, Bearer auth).
created_atstring
updated_atstring

invoice_transaction

FieldTypeDescription
typeinteger4 Check, 5 Card charge, 6 ACH, 7 Refund, 8 Account credit, 9 Account debit.
statusinteger1 Open, 2 Authorized, 3 Captured, 4 Settled, 5 Expired, 6 Refunded, 7 Void, 8 Reversed, 9 Initiated (ACH pending), 10 Failed.
amountintegerCents.
referencestring | nullProvider charge / intent id or check number.
created_atstring

line_item_request

FieldTypeDescription
artworkobjectTransfer artwork reference — {uuid} (preferred), {tin}, or {name}. A bare uuid string is also accepted. Omit for stock SKUs.
Example {"uuid":"3f1c2a9e-\u2026"}
skustringStock SKU, or a specific transfer size SKU to pin (must be the size recommended for the artwork).
quantityrequiredintegerTransfers in the artwork's own colours (the base colourway).
Example 144
color_changesarray of color_changeAdditional 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_daysinteger0–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_skustringYour own SKU, echoed on the order and invoice.
customer_sales_orderstringYour own sales order reference for this line.

master

FieldTypeDescription
design_uuidstring | nullUuid 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).
scopestring | nullDesign 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_customerobject | null{uuid, name} of the customer a customer-scoped master belongs to.
categoriesarray of stringGallery category names.
variablesarray of master_variableText layers derivatives substitute, in document order.
mutator_rulesarray of mutator_ruleOrdered waterfall of variable mutators applied to every derivative data row before substitution.
rotationsarray of print_rotationAuthored press passes (one per separation, plus the HA ADHESIVE powder pass when inks touch).
fontsarray of stringFont faces the document uses (<Family>-<Style>).
derivative_countintegerCurrent-version derivatives generated from this master.
document_urlstring | nullAPI URL returning the design document (staff keys).

master_variable

FieldTypeDescription
keystringVariable name — the data column derivatives bind to this layer.
layer_idstringId of the text layer in the design document.
defaultstring | nullPlaceholder text the layer carries in the master (what the compiled PDF shows).

mutator

FieldTypeDescription
namestringValue to send as mutator_rules[].mutator.
labelstringHuman label.
paramsarray of mutator_paramParameters the mutator accepts (param keys). Empty when it takes none.

mutator_param

FieldTypeDescription
keystringKey inside param.
labelstring
typestringnumber → integer; text → string; select → one of options.
One of: number, text, select
defaultstring | nullValue used when the key is omitted.
optionsarray of stringAllowed values for select.

mutator_rule

FieldTypeDescription
mutatorstringRegistered mutator name — see GET /v1/artwork/mutators.
variable_instringVariable the mutator reads (and, for most, the prefix of the fields it writes, e.g. Name → Name_Line1, Name_Line2). CreateField writes this key.
paramobject | nullMutator parameters per its params schema (e.g. {"n": 1}); null when it takes none.
sort_orderintegerPosition in the waterfall (0-based). Rules run in this order.

order

FieldTypeDescription
uuidstring
idstringHuman order number (S#####).
statusintegerOne of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
status_textstring1 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_orderstring | null
notesstring | null
total_unitsinteger
subtotal_amountintegerCents.
shipping_amountintegerCents.
discount_amountintegerCents.
tax_amountintegerCents.
total_amountintegerCents. subtotal - discount + shipping + tax.
currencystringAll amounts on the order and its line items are integer minor units of this currency.
Example USD
turn_daysinteger
ship_datestring | nullScheduled ship date (Y-m-d).
delivery_datestring | null
shippingobject{carrier, service, account_number} — account_number present only when shipping on your own account.
ship_tocontact
bill_tocontact
line_itemsarray of order_line_item
shipmentsarrayShipment summaries (see GET /v1/shipments).
invoicesarrayInvoice summaries (see GET /v1/invoices).
print_spec_urlstring | nullMockup / print spec PDF.
created_atstring
updated_atstring

order_line_item

FieldTypeDescription
idinteger
descriptionstring
skustring | null
quantityintegerTotal transfers on the line across all colourways.
turn_daysinteger
unit_priceintegerCents. Per-piece price excluding services.
total_service_priceintegerCents. Setup + colour upcharges + colour changes + rush for the line.
total_priceintegerCents.
customer_skustring | null
customer_sales_orderstring | null
artworkarray[{uuid, name, tin, status, status_text}] — the base artwork first, then one artwork per colour change.
colorwaysarray[{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.
servicesarray[{id, name, quantity, unit_price, total_price}] — 1 custom colour, 2 setup, 3 rush, 366 colour change (quantity = changed inks billed).

order_request

FieldTypeDescription
customer_purchase_orderstringYour PO number (255 chars max). Echoed on the invoice.
Example PO-10422
notesstringFree-form production notes.
ship_torequiredship_to
line_itemsrequiredarray of line_item_request
shippingrequiredshipping_request
paymentpayment_requestOptional for accounts with payment terms (defaults to terms).

pantone_color

FieldTypeDescription
idintegerStable platform color id — the same id color_ids accepts on pricing/order specs.
Example 8612
namestringHuman-readable ink name.
Example Red
pmsstringPantone 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
hexstring | nullWeb hex value, #RRGGBB.
Example #E4002B
rgbobject | null{r, g, b}, each 0–255. null when the ink has no RGB values on file.
cmykobject | null{c, m, y, k}, each 0–100. null when the ink has no CMYK values on file.
is_specialbooleanSpecialty ink (metallic, glitter, etc.).
is_semiopaquebooleanSemi-opaque ink — needs a base or special layering over dark substrates.
updated_atstringUTC ISO 8601 last-modified stamp for this row.
Example 2026-09-01T14:03:22Z

payment_method

FieldTypeDescription
uuidstringPass as payment.payment_method on POST /v1/orders.
typestringOne of: card, bank_account
labelstring | null
is_defaultboolean
brandstring | nullCards: Visa, Mastercard, …
last4string | null
exp_monthinteger | nullCards only.
exp_yearinteger | nullCards only.
bank_namestring | nullBank accounts only.
account_holder_namestring | nullBank accounts only.
routing_numberstring | nullBank accounts only.
statusstring | nullBank accounts: new, pending (micro-deposits sent), verified. Unverified accounts cannot be charged.
usablebooleanTrue when the method can be charged by POST /v1/orders right now.
billing_contactcontact
created_atstring

payment_method_list

FieldTypeDescription
dataarray of payment_method
terms_enabledbooleanAccount may pay by terms (payment.method: terms).
check_enabledbooleanAccount may pay by check (payment.method: check).
payment_terms_daysintegerNet terms in days when terms are enabled.

payment_request

FieldTypeDescription
methodstringDefaults 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_methodstringUUID 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_codestringCheck number / reference (method check only).

print_file

FieldTypeDescription
idintegerProduction file id.
artwork_uuidstringCurrent uuid of the artwork the file belongs to.
print_method_idintegerPrint method the file was prepared for (1 = Screen Printing Sheet-Fed).
print_methodstring | nullPrint method name.
versionintegerPer print-method version; each attach increments it and production always uses the highest.
namestringDisplay name (defaults to the artwork name).
image_widthdecimal | nullTrimmed printed-image width in inches, measured from the file.
image_heightdecimal | nullTrimmed printed-image height in inches.
document_widthdecimal | nullPage (document) width in inches.
document_heightdecimal | nullPage (document) height in inches.
formatstringAlways pdf.
preview_png_urlstring | nullPublic low-res PNG preview of the production file.
uploaded_byobject | null{uuid, name} of the staff user whose key attached the file.
created_atdatetime

print_rotation

FieldTypeDescription
indexintegerPress pass number (1-based).
colorcolor | nullInk of the pass; null for the HA ADHESIVE powder pass.
alternate_color_namestring | nullNamed pass with no colour row (HA ADHESIVE).
ink_typestringPlastisol colour pass or Clear powder pass.
adhesive_typestring | nullPowder adhesive applied on this pass (Dakota / 16P / 1P by formula); null when a dedicated powder pass carries it.
screen_mesh_countintegerScreen mesh.

quote

FieldTypeDescription
artworkobject | null{uuid, name, tin} when quoted from an artwork.
specdesign_specThe specification that was priced.
matchedobject | nullTransfer 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.
quantityintegerBase colourway quantity as requested.
total_quantityintegerquantity + Σ color_changes[].quantity — the quantity the design was priced at (all colourways share one price break).
currencystringAlways USD. Every money field below is an integer in minor units (cents).
unit_priceintegerCents. All-in price per transfer at total_quantity: piece price + setup and one-time fees amortised. Excludes rush and colour-change fees.
subtotalintegerCents. Transfers + setup + one-time fees (unit_price × total_quantity).
breakdownarrayItemised rows {label, quantity, unit_price, total_price} (cents) that sum to subtotal.
color_changesobject{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.
rushobject{enabled, days, unit_fee, fee} — fees in cents; enabled tells you whether the account may request rush at all.
totalintegerCents. subtotal + color_changes.total_fee + rush.fee. Excludes shipping and tax.
tiersarrayStandard quantity breaks {quantity, price_each, price_total} (cents) at 72, 144, 288, 512, 1024 for the same design.
turn_daysintegerBusiness days to ship after rush is applied.
estimated_ship_datedate | nullShip date if ordered now (Y-m-d, business-day calendar).
has_custom_pricingbooleanTrue when negotiated account pricing was applied.

quote_request

FieldTypeDescription
artworkstringUUID 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
specdesign_specDesign specification to quote when you do not have an artwork yet. Ignored when artwork is given.
quantityrequiredintegerNumber of transfers (≥ 1).
Example 144
rush_daysintegerDays 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_changesarray of color_changeAdditional colourways of the same design (see color_change). quantity is the base colourway; the design is priced at quantity + Σ color_changes[].quantity.

ship_to

FieldTypeDescription
namerequiredstringExample Acme Screen Printing
attentionstringExample Receiving
street1requiredstringExample 123 Main St
street2string
street3string
cityrequiredstringExample Richmond
statestringRequired for US/CA destinations.
Example VA
postal_coderequiredstringExample 23220
countryrequiredstringISO 3166-1 alpha-2.
Example US
email_addressrequiredstringExample receiving@acme.example
phone_numberstringRequired unless mobile_number is given.
Example +18045551234
mobile_numberstring
fax_numberstring

shipment

FieldTypeDescription
idstringShipment number (P######). Use in GET /v1/shipments/:id.
statusintegerOne of: 1, 2, 3, 4, 5, 6, 7
status_textstring1 Packaged, 2 Shipped, 3 Delivered, 4 Canceled, 5 Void, 6 Ready for Pickup, 7 Picked Up.
carrierstring | nullExample UPS
servicestring | nullExample UPS GROUND
account_numberstring | nullYour carrier account when shipped third-party billed.
tracking_numbersarrayDistinct tracking numbers across boxes.
ship_tocontact
ordersarray[{uuid, id}] orders carried by this shipment.
boxesarray of shipment_box
scheduled_pickup_datestring | null
pickup_datestring | null
scheduled_delivery_datestring | null
delivered_atstring | null
created_atstring
updated_atstring

shipment_box

FieldTypeDescription
tracking_numberstring | null
tracking_urlstring | nullCarrier tracking page for the box.
lengthdecimal | null
widthdecimal | null
heightdecimal | null
dimension_unitstring | nullExample IN
weightdecimal | null
weight_unitstring | nullExample LBS
line_itemsarray[{order_line_item_id, quantity}] — what is packed in the box.

shipping_options

FieldTypeDescription
carriersarrayCarriers accepted in shipping.carrier.
servicesobjectCarrier → list of accepted shipping.service names.
methodsarrayshipping.method values this account may use (rate, plus account / pickup when enabled).
live_rate_carriersarrayCarriers that support method: rate (currently UPS).

shipping_rate

FieldTypeDescription
carrierstringExample UPS
servicestringPass as shipping.service on the order.
Example UPS GROUND
service_codestringExample 03
amountintegerCents (currency USD); what the order will bill for shipping. 0 for delayed-billing accounts.
estimated_daysintegerBusiness days in transit after pickup.
estimated_delivery_datestring | nullY-m-d.

shipping_rates

FieldTypeDescription
carrierstringExample UPS
pickup_datedateProduction ship date used for rating.
turn_daysinteger
subtotal_amountintegerCents. Merchandise subtotal used as declared value.
currencystringAlways USD; all amounts are integer cents.
ratesarray of shipping_rateSorted cheapest first.

shipping_rates_request

FieldTypeDescription
ship_torequiredship_to
line_itemsrequiredarray of line_item_request

shipping_request

FieldTypeDescription
methodstringrate — 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
carrierstringCarrier name. Only UPS supports rate.
Default UPS
servicestringService 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_numberstringYour carrier account number (required for account).

ticket

FieldTypeDescription
uuidstring
idstringZero-padded ticket number shown in the admin console, e.g. 0057452.
titlestring
summarystring | nullTicket body HTML. Listing returns it truncated to 500 characters.
typeintegerOne of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
type_textstring1 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.
statusintegerOne of: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
status_textstring1 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.
priorityintegerOne of: 1, 2, 3, 4, 127
priority_textstring1 High, 2 Medium, 3 Low, 4 Shelved, 127 None.
classificationinteger | nullStaff keys only. 1 Top Secret, 2 Secret, 3 Confidential, 126 Declassified, 127 Unclassified.
classification_textstring | nullStaff keys only.
is_privatebooleanStaff keys only; customer keys never see private tickets.
due_atstring | nullServer-time Y-m-d H:i:s.
is_all_daybooleanWhether due_at is a whole-day deadline.
knowledgebase_urlstring | nullPublic article URL when the ticket is a Published Article tagged knowledgebase or faq.
knowledgebase_preview_urlstring | nullFor 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_ticketobject | null{uuid, title} of the parent ticket.
orderobject | null{uuid, id} of the linked order.
artworkobject | null{uuid, name, tin} of the linked artwork.
itemobject | nullStaff keys only. {uuid, name} of the linked warehouse item.
tagsarray of stringTag names.
customerobject | nullStaff keys only. {uuid, organization, name} of the affected customer account.
assigneeobject | null{uuid, name} of the staff member working the ticket.
project_managerobject | nullStaff keys only. {uuid, name}.
delegatorobject | nullStaff keys only. {uuid, name} of whoever assigned the ticket.
creatorobject | nullStaff keys only. {uuid, name}.
followersarray of ticket_userStaff keys, detail only.
messagesarray of ticket_messageDetail only. Chronological thread — customer keys see their own messages and staff replies sent to them.
filesarray of ticket_fileDetail only. Attachments on the ticket itself (customer keys: the account's own uploads).
childrenarrayDetail only. [{uuid, title, status, status_text}] of child tickets visible to the account.
created_atstring
updated_atstring

ticket_file

FieldTypeDescription
uuidstring
namestring
mimetypestring | null
sizeinteger | nullBytes.
downloadstringAuthenticated download URL (GET /v1/tickets/:uuid/files/:fuuid/download).
urlstring | nullPublic URL on the store domain for knowledgebase Article assets (/assets/knowledgebase/<ticket uuid>/<name>); null for private support-ticket attachments.
created_atstring

ticket_message

FieldTypeDescription
uuidstring
senderobject | null{uuid, name} of the author (null for inbound email / system events).
fromstring | nullEmail display name for inbound mail.
subjectstring | null
bodystring | nullMessage body HTML.
is_systembooleanTrue for automated thread events.
filesarray of ticket_fileAttachments on this reply.
created_atstring

ticket_tag

FieldTypeDescription
uuidstring
namestring

ticket_user

FieldTypeDescription
uuidstring
namestring

verify_request

FieldTypeDescription
amount1requiredintegerFirst micro-deposit in cents (1–100), e.g. 32 for $0.32.
Example 32
amount2requiredintegerSecond micro-deposit in cents (1–100).
Example 45

Webhooks

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.

Envelope

FieldTypeDescription
idstringDelivery UUID (unique per event delivery).
typestringEvent type, e.g. order.created.
createdintegerUnix timestamp the event was emitted.
dataobject{ "object": {...} } — the object payload for the event.

Headers

X-Webhook-EventEvent type.
X-Webhook-DeliveryDelivery UUID (matches body id).
X-Webhook-Signaturet=<unix_ts>,v1=<hex HMAC-SHA256(secret, "<t>.<raw_body>")>
User-AgentEpicERP-Webhook/1.0

Signature verification

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;

Retries

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.

Event catalog

GroupEventWhen
Ordersorder.createdAn order was placed (checkout or API).
Ordersorder.status_changedAn order moved to a new status.
Ordersorder.shippedAll shipments for an order have been created.
Ordersorder.canceledAn order was canceled.
Shipmentsshipment.createdA shipment (with carrier + tracking number) was created.
Shipmentsshipment.tracking_updatedCarrier tracking status changed for a shipment.
Artworkartwork.createdNew artwork was uploaded to the library.
Artworkartwork.preflight_passedPreflight approved the artwork for production.
Artworkartwork.preflight_failedPreflight rejected the artwork; see reasons in data.
Artworkartwork.approval_requestedArtwork is waiting on your approval (Pending Customer Approval).
Artworkartwork.archivedArtwork was archived.
Artworkproof.readyA proof is ready for review/approval.
Invoicesinvoice.createdAn invoice was generated.
Invoicesinvoice.paidAn invoice was paid in full.
TestingtestSent by the "Test" button in Settings → Webhooks.