REST API
The TronDesigner REST API lets your server read and upload logos, read print jobs, download preview images, proofreading PDFs and print data, generate new proofreadings and print data, render logo visualizations, and create signed editor links. It's a server-to-server API: call it from your backend, ERP or integration middleware, never from the browser.
TIP
Reference: every endpoint, parameter and response model is in the Swagger UI (OpenAPI JSON: https://api-v1.trondesigner.com/swagger/v1/swagger.json).
Choose the right integration
Integration script (window.tronDesigner) | REST API | |
|---|---|---|
| Runs in | Your customer's browser | Your server |
| Key | Website integration API key (public, restricted by allowed domains) | REST API key (secret, optionally restricted by IP address) |
| Typical use | Open the editor, react to events, show images on your pages | Fetch print jobs after checkout, download proofreading and print data, automate ERP flows, generate editor links for e-mails |
Most integrations use both: the script captures the printJobGuid in the browser (see Events and callbacks), and your server later uses the REST API to fetch the files it needs. For browser-side downloads, see Retrieving print jobs and files.
Before you start
- License. Your TronDesigner license must include API Access (all read and download endpoints) and, for the endpoints that generate something, API Automation; rendering logo visualizations needs API Visualizations. If a feature is missing, the Admin Portal says so on the REST API Keys tab and the API answers
401 Unauthorized. - Base URL:
https://api-v1.trondesigner.com/api. Paths below are relative tohttps://api-v1.trondesigner.com. - Format. Requests and responses are JSON with camelCase property names. Enums are strings (for example
"state": "ProofApproved"), dates are ISO 8601. - Language. Send
Accept-Language(en-US,cs-CZ,fr-FR,de-DE,es-ESorit-IT) to choose the language of localized texts and the default language of generated documents. Only the first value is used; the default is English.
Create a REST API key
In the Admin Portal open Settings → API Keys and switch to the REST API Keys tab.
Create a key, give it a name and pick its roles:
Role What it allows Read Access (TronDesigner) — TronDesigner.RestApiKey.ReadAccessRead and download logos, print jobs and print visualizations. For apps that only consume existing data. Update Access (TronDesigner) — TronDesigner.RestApiKey.UpdateAccessCreate, update and delete logos, print jobs and print visualizations. For apps that generate proofreadings or print data. Optionally add Allowed IP addresses. When the list isn't empty, only requests from exactly these addresses are accepted (no ranges).
Copy the key from the confirmation dialog. It is shown only once. If you lose it, edit the key and choose Generate new key — the old value stops working immediately.
Use View Usage on a key to see its request log (time, method, URL, IP address), and switch Active off to disable a key without deleting it.
Authenticate requests
Send the key as a bearer token in the Authorization header of every request. POST requests with a JSON body also need Content-Type: application/json (otherwise the API returns 415). The logo upload is the one exception: it takes multipart/form-data.
curl https://api-v1.trondesigner.com/api/logos?pageSize=1 \
-H "Authorization: Bearer $TD_REST_API_KEY"A 200 OK with a JSON page of logos means the key, roles and license are set up correctly. The REST API ignores the TronDesigner-ApiKey header used by the integration script.
Endpoints
| Group | Method and path | Returns | License feature |
|---|---|---|---|
| Print jobs | GET /api/print-jobs | Paged list | API Access |
GET /api/print-jobs/{printJobGuid} | Print job detail | API Access | |
| Images | GET /api/print-jobs/{printJobGuid}/{printingGuid}/motive-image | Motive image (the designed graphics) | API Access |
GET /api/print-jobs/{printJobGuid}/{printingGuid}/{variantGuid}/preview-image | Product variant preview; printAreaImageType = MOCKUP, MOCKUP_CLEAR (default), VISUALIZATION or AI_MOOD_SCENE | API Access | |
| Proofreading | POST /api/print-jobs/{printJobGuid}/proofreading | { "proofreadingGuid" } | API Automation |
GET /api/print-jobs/{printJobGuid}/proofreading/list | All proofreadings (with dataUrl) | API Access | |
GET /api/print-jobs/{printJobGuid}/proofreading/last · …/proofreading/{proofreadingGuid} | PDF file | API Access | |
| Print data (print job) | POST /api/print-jobs/{printJobGuid}/print-data | { "printDataGuid" } | API Automation |
GET /api/print-jobs/{printJobGuid}/print-data/list | All print data (with dataUrl) | API Access | |
GET /api/print-jobs/{printJobGuid}/print-data/last · …/print-data/{printDataGuid} | PDF file | API Access | |
| Print data (one printing) | POST /api/print-jobs/{printJobGuid}/printing/{printingGuid}/print-data | PDF file (in the response) | API Automation |
GET …/printing/{printingGuid}/print-data/list · …/last · …/{printDataGuid} | List or PDF file | API Access | |
| Logos | GET /api/logos · GET /api/logos/{logoGuid} | Paged list · logo detail | API Access |
GET /api/logos/{logoGuid}/file | Logo file; fileType = ORIGINAL, CONVERTED (default) or PREVIEW | API Access | |
POST /api/logos (multipart/form-data) · GET /api/logos/{logoGuid}/upload-status | Upload a logo (asynchronous, see Upload a logo) · its processing status | API Automation · API Access | |
POST /api/logos/{logoGuid}/recolor | A new, recolored logo, see Recolor a logo | API Automation | |
| Visualizations | GET /api/visualizations/render | JPEG image of a logo on a product, see Render a logo visualization | API Visualizations |
| Editor links | POST /api/integration-site-links/generate-open-editor-link | { "linkToken", "link" } | API Automation |
List endpoints accept page (default 1), pageSize (default 10), requireTotalCount, requireGroupCounts, search, createdFrom and createdTo; print jobs also filter by states, logos by type and customerGuid.
Get a print job
Use the printJobGuid you stored from the printJob created event or a webhook.
curl https://api-v1.trondesigner.com/api/print-jobs/76b2be59-6403-4f89-9c9a-3a64588c05d3 \
-H "Authorization: Bearer $TD_REST_API_KEY" \
-H "Accept-Language: en-US"{
"printJobGuid": "76b2be59-6403-4f89-9c9a-3a64588c05d3",
"code": "PJ00000069",
"state": "ProofApproved",
"customer": { "customerGuid": "a70b77d6-…", "orderCode": "ORDER-1001", "orderItemCode": "1" },
"product": { "title": "Oregon 400 ml", "variants": [ … ] },
"printings": [
{
"printingGuid": "5f60a5a1-4575-481a-90ad-7b0b11203aa8",
"printData": { "positionCodeDisplay": "front", "technologyCodeDisplay": "PAD03", … },
"printMotive": {
"motiveImage": { "printJobFileUrl": "https://api-v1.trondesigner.com/api/print-jobs/76b2…/5f60…/motive-image", … },
"printAreaImages": [ … ]
}
}
],
"customProperties": { "cartLineId": "CL-4711" }
}(Shortened.) The printJobFileUrl values point back to the REST API image endpoints, so request them with the same Authorization header.
Upload a logo
Processing a logo (parsing, color detection, conversion for print) takes from under a second to a minute, so the upload is asynchronous: the API accepts the file, answers right away, and you find out the result separately.
curl -X POST https://api-v1.trondesigner.com/api/logos \
-H "Authorization: Bearer $TD_REST_API_KEY" \
-H "Idempotency-Key: order-1001-logo-1" \
-F "File=@company-logo.eps" \
-F "Name=Company logo" \
-F "CustomerGuid=a70b77d6-1f0e-4a8b-9c1d-2e3f4a5b6c7d" \
-F "CustomerCompany=ACME Ltd." \
-F "WaitSeconds=10"| Form field | Required | Meaning |
|---|---|---|
File | yes | The logo file, at most 20 MB (20 971 520 bytes). Only the formats allowed for your account are accepted — the 400 response lists them. |
Name | no | Display name. Replaces the base of the file name and keeps the extension: logo.eps with name Company logo becomes Company logo.eps. |
CustomerGuid | no | Your identifier of the end customer the logo belongs to. Omit it for a logo shared by your whole account. |
CustomerPerson, CustomerCompany, CustomerEmail, CustomerIdentity | no | Contact data of that customer. Ignored without CustomerGuid. |
WaitSeconds | no | 0–30, default 0. Hold the response up to this many seconds until processing finishes. |
Read the result
| Response | Meaning |
|---|---|
202 Accepted | Processing has started. The body has "status": "Queued" or "Processing", the Location header and statusUrl point to the status resource, and Retry-After says how many seconds to wait before asking. |
200 OK | Processing has already finished — within WaitSeconds, or because the file was uploaded before. It doesn't mean it succeeded: check status, which is Completed or Failed. |
400, 413 | The file was rejected before processing: no or empty file, a format your account doesn't allow, an invalid field — or 413 for a file over 20 MB. Nothing was created. |
{
"logoGuid": "6993afa3-f416-4af3-9a14-2ce7e3c774f8",
"status": "Queued",
"progress": 0,
"deduplicated": false,
"statusUrl": "https://api-v1.trondesigner.com/api/logos/6993afa3-f416-4af3-9a14-2ce7e3c774f8/upload-status",
"logo": null
}Keep the logoGuid — it identifies the logo from the first response on. While status is Queued or Processing, poll GET /api/logos/{logoGuid}/upload-status, waiting Retry-After seconds between requests. Completed and Failed never change again:
response = POST /api/logos (with WaitSeconds=10)
while response.status in (Queued, Processing):
sleep(response.headers["Retry-After"] or 2 seconds)
response = GET response.statusUrl
if response.status == Completed: use response.logo
else: report response.message / response.messageCodeupload-status returns the same logoGuid, status and progress, plus:
message— what is happening or why it failed, in the language ofAccept-Language;messageCode— the same as a fixed code, for exampleLogoUpload.UploadEvents.ProcessingFailed. React to this one in code,messagecan change;created,updated— when the upload was submitted and when its state last changed;logo— the finished logo (the same asGET /api/logos/{logoGuid}) oncestatusisCompleted, otherwisenull.
WaitSeconds saves you the polling for most files, but always handle a 202: a large or complex file can take longer, and then you get the usual 202 and processing continues.
Failed uploads
A file that can't be processed (for example a damaged or empty graphic) ends with "status": "Failed". No logo is created: GET /api/logos/{logoGuid} answers 404, and the failure is visible only in the upload and status responses. The status of an upload is kept for 24 hours; after that a failed upload answers 404 as well, while a completed one keeps answering Completed.
Retries and duplicates
Idempotency-Key(header, optional but recommended). Any string up to 255 characters, remembered for 24 hours. Repeating the key returns the state of the original upload instead of uploading again — even when the repeated request carries a different file. Use one key per logo you mean to upload, for example derived from your order line.- The same file is stored only once per customer. Uploading a file that already exists returns
200with"deduplicated": trueand thelogoGuidof the existing logo, whatever name you send. - Webhooks. A successfully stored logo sends the
logo.createdwebhook; a deduplicated or failed upload doesn't. The webhook also fires for logos created by edits (a recolor, edits in the editor), so match itsLogoGuidwith thelogoGuidyou got here. Never rely on the webhook alone — always read the upload response.
Get a logo
curl https://api-v1.trondesigner.com/api/logos/6993afa3-f416-4af3-9a14-2ce7e3c774f8 \
-H "Authorization: Bearer $TD_REST_API_KEY"{
"version": "1.0",
"logoGuid": "6993afa3-f416-4af3-9a14-2ce7e3c774f8",
"originalLogoGuid": null,
"previousLogoGuid": null,
"customer": {
"customerGUID": "00000000-0000-0000-0000-000000000000",
"customerPerson": null, "customerCompany": null, "customerEmail": null, "customerIdentity": null
},
"created": "2026-10-15T05:24:09.3307471+00:00",
"updated": null,
"filename": "Company logo.svg",
"name": "Company logo.svg",
"type": "VECTOR",
"state": "Active",
"colors": {
"isFullColor": false,
"colorList": [
{ "mapHex": "#FF0000FF", "pmsName": "PANTONE 185 C", "cmykHex": "#00FFD114", "rgbHex": "#EB002AFF", "source": "RGB" },
{ "mapHex": "#0000FFFF", "pmsName": "PANTONE 2736 C", "cmykHex": "#E0C50053", "rgbHex": "#1527ACFF", "source": "RGB" }
],
"shouldUseDarkBackground": false
},
"logoUrls": {
"original": "https://api-v1.trondesigner.com/api/logos/6993afa3-…/file?fileType=ORIGINAL",
"converted": "https://api-v1.trondesigner.com/api/logos/6993afa3-…/file?fileType=CONVERTED",
"preview": "https://api-v1.trondesigner.com/api/logos/6993afa3-…/file?fileType=PREVIEW"
},
"dimensions": { "size": { "unit": "PIXELS", "width": 200, "height": 100 } },
"properties": [ { "classPath": "HasTransparency", "value": "True" } ]
}customer— acustomerGUIDof all zeros means the logo belongs to your whole account, not to a specific end customer.type—VECTOR(SVG, PDF, EPS, AI, …) orRASTER(PNG, JPG, TIFF, …).state—Activefor regular logos, whichGET /api/logoslists.Hiddenfor results of edits such as a recolor: readable by their GUID, but not listed.originalLogoGuid,previousLogoGuid— for a logo derived from another one (recolor), the first logo of the chain and the direct predecessor;nullfor an uploaded logo.colors—isFullColoristruefor photos and gradients, which have too many colors to separate;colorListis then empty. Otherwise each color comes in every color system:mapHexas it is in the file, the matched PantonepmsName,cmykHex(8 hex digits, C, M, Y, K) andrgbHex(the screen color of the matched print color, so it can differ frommapHex).shouldUseDarkBackgroundistruefor mostly light logos (e.g. a white logo) that are invisible on white — show their preview on a dark background.nullfor logos uploaded before this was added.logoUrls— download links; send the sameAuthorizationheader.ORIGINALis the file as uploaded,CONVERTEDthe file prepared for print,PREVIEWan image for display.properties— detected characteristics (HasTransparency,HasGradient,HasRasterImage, …), usually with the value"True"or"False". Ignore names you don't know, more may be added.
An unknown logo, a logo of another account and a failed upload all answer 404.
Recolor a logo
Replace colors of a logo, for example to print it in the customer's brand Pantone. Recoloring always creates a new logo — the source logo stays untouched.
curl -X POST https://api-v1.trondesigner.com/api/logos/6993afa3-f416-4af3-9a14-2ce7e3c774f8/recolor \
-H "Authorization: Bearer $TD_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "PMS",
"colors": [
{ "valueFrom": "PANTONE 185 C", "valueTo": "PANTONE Yellow C" },
{ "valueFrom": "PANTONE 2736 C", "valueTo": null }
]
}'type— the color system of all values in the request:PMS(Pantone names such as"PANTONE Yellow C"),CMYK("#"and 8 hex digits, two per channel C, M, Y, K, e.g."#0021FF00") orRGB("#"and 6 hex digits, or 8 with alpha, e.g."#FFDE00"). For print, preferPMSorCMYK.valueFrom— a color the logo really has, read fromcolors.colorListof the logo:pmsNameforPMS,cmykHexforCMYK,rgbHexforRGB. Matching ignores case and, for RGB, an opaqueFFalpha.valueTo— the new color, ornullto remove the color from the logo. Colors you don't list stay as they are.
Several colors of the file can match the same Pantone, so colorList can contain the same pmsName (and cmykHex) more than once. A PMS or CMYK recolor of that value replaces all of them.
The response is the new logo, the same model as GET /api/logos/{logoGuid}, with "state": "Hidden", originalLogoGuid and previousLogoGuid set. Keep its logoGuid: a hidden logo doesn't appear in GET /api/logos, so the GUID is the only way back to it. Use it like any other logo, for example in visualizations or editor links.
| Response | When |
|---|---|
200 OK | The new logo. |
400 Bad Request | The request can't be applied: an unknown type; no colors; an empty or repeated valueFrom, or one the logo doesn't have (the message lists the logo's colors); a valueTo that isn't valid in the color system, such as an unknown Pantone name; nothing would change; every color would be removed; the logo is full color (isFullColor: true) and has no separate colors to replace. |
404 Not Found | The logo doesn't exist or belongs to another account. |
504 Gateway Timeout | Recoloring didn't finish in time. Retrying is safe, but may leave an extra hidden logo behind. |
- Recoloring runs while the request waits — usually a fraction of a second, but tens of seconds for a complex vector. The API gives up after 100 seconds with
504, so set your HTTP client timeout above that. - Each call creates another logo, even when it is identical to an earlier one. The new logo sends the
logo.createdwebhook.
Generate and download a proofreading
Generation runs synchronously: the POST returns once the PDF exists.
# 1. Generate (body is optional; the language defaults to Accept-Language)
curl -X POST https://api-v1.trondesigner.com/api/print-jobs/$PRINT_JOB_GUID/proofreading \
-H "Authorization: Bearer $TD_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "proofreadingLanguageIso2Code": "en" }'
# → { "proofreadingGuid": "435b29be-3841-44e3-80c6-6d5adc74456e" }
# 2. Download it (or use /proofreading/last)
curl https://api-v1.trondesigner.com/api/print-jobs/$PRINT_JOB_GUID/proofreading/435b29be-3841-44e3-80c6-6d5adc74456e \
-H "Authorization: Bearer $TD_REST_API_KEY" -o proofreading.pdftemplateTag selects a proofreading template by its tag. Print data works the same way: POST …/print-data with { "languageIso2Code": "en", "templateTag": null, "timeZone": "Europe/Prague" } returns { "printDataGuid" }, then GET …/print-data/last downloads the PDF. timeZone is an IANA name used for dates in the document (default UTC).
Create an editor link
Generate a signed link that opens the editor on the TronDesigner Integration Site without exposing any key in the URL — for example in an e-mail or from your ERP. options takes the editor arguments (EditorArguments), see Token links. The full schema is GenerateIntegrationSiteLinkRequestDto in Swagger.
curl -X POST https://api-v1.trondesigner.com/api/integration-site-links/generate-open-editor-link \
-H "Authorization: Bearer $TD_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"options": {
"openMode": "Create",
"dataMode": "Exact",
"printJob": {
"addProducts": { "supplierCode": "PFC", "variants": [{ "productCode": "10086021" }] },
"addPrintings": [{ "printPositionCode": "front", "printTechnologyCode": "PAD03" }]
}
},
"expiresAt": "2026-12-31T23:59:59Z"
}'
# → { "linkToken": "eyJhbGciOi…", "link": "<Integration Site URL>?token=eyJhbGciOi…" }apiKey(optional) — the website integration key the editor session uses. If you omit it, the key tagged Integration Site in Settings → API Keys is used.expiresAt(optional) — the link stops working after this time. Without it the link doesn't expire.returnUrl(optional) — absolutehttp(s)URL that the Integration Site opens when the editor closes.
To edit an existing design, send "openMode": "Edit" and "printJob": { "printJobGuid": "…" }.
Put customer and order data into printJob.customer. A customer object next to printJob (the old schema) is still accepted and moved there.
Render a logo visualization
Render one of your logos onto a product variant — for example for product feeds, e-mails or offers — the server-side counterpart of the integration script's logo visualizations. The response is the JPEG image itself.
curl "https://api-v1.trondesigner.com/api/visualizations/render?logoGuid=$LOGO_GUID&supplierCode=TD&variantCode=10601353_TD&printPositionCode=FRONT&printTechnologyCode=DPRINT03&width=600&height=600" \
-H "Authorization: Bearer $TD_REST_API_KEY" -o visualization.jpg| Parameter | Required | Meaning |
|---|---|---|
logoGuid | yes | A logo of your instance (from GET /api/logos or an upload). |
supplierCode or supplierGuid | one of them | supplierCode must be one of the codes in Settings → Suppliers of the Admin Portal. |
variantCode | yes | The supplier's product variant code. |
printPositionCode, printTechnologyCode | no | The print option to visualize. Without them TronDesigner picks a print option of the variant. |
width, height | no | Size the image fits into, 1–4096 px. Default 300 × 300. |
visualization2DMode | no | Visualization source: RealisticFallbackGenerated (default — a realistic visualization set up for the print option, otherwise one generated from the product's print mockup), RealisticOnly or GeneratedOnly. |
- The key needs the
TronDesigner.Products.Print.Visual.Readpermission. - When there is nothing to render — no source the mode allows, an unknown variant or supplier, or a missing logo file — the API answers
404 Not Foundwith a short text such as Visual not found. WithoutsupplierCodeandsupplierGuidit answers400 Bad Request.500 Internal Server Errormeans that rendering failed, and a retry may succeed. Error responses aren't cached. - The Settings → Logo Visualizations of the Admin Portal (source, watermark) apply to the integration script only; this endpoint takes the source from
visualization2DModeand renders without a watermark. - Each successful render counts as one visualization towards the 2D Visualizations quota of your license, the same as the integration script's renders. Failed requests don't. A rendered image may be cached for one hour (
Cache-Control: private, max-age=3600) — cache the images on your side instead of rendering the same image repeatedly.
Errors
| Status | Meaning |
|---|---|
400 Bad Request | Invalid request body or parameters (for example an unknown apiKey when creating an editor link). |
401 Unauthorized (no body) | Missing or malformed Authorization header, unknown or disabled key, or the caller's IP address isn't in the key's allowed list. |
401 Unauthorized (JSON body) | The key is valid but lacks the required role, or the license lacks API Access / API Automation. |
404 Not Found | The print job or logo doesn't exist (or belongs to another instance), the upload status is unknown or expired, the requested file (image, proofreading, print data) doesn't exist, or there is no logo visualization to render. |
413 Payload Too Large | The uploaded logo file is larger than 20 MB. |
415 Unsupported Media Type | A JSON request body was sent without Content-Type: application/json, or the logo upload wasn't sent as multipart/form-data. |
500 Internal Server Error | Unexpected error — retry later or contact PromoTron support with the request time. |
504 Gateway Timeout | A logo recolor didn't finish within 100 seconds. |
Errors raised by TronDesigner have a JSON body with exceptionType (Validation, Authorization, ApplicationError or UnhandledError), title.message (a message or a message key such as BackEnd.Common.ErrorMessages.UnauthorizedMissingPermission) and a messages array with details:
{ "exceptionType": "Validation", "title": { "message": "The waitSeconds form field must be between 0 and 30.", "messageParameters": {} }, "messages": [], "url": null }When the request itself can't be read — malformed JSON, a value of the wrong type such as WaitSeconds=abc, an unknown enum value or a missing required field — messages lists each problem, and messageParameters.field names the form or query field (waitSeconds) or the JSON path ($.colors[0].valueFrom; $ for the body as a whole):
{
"exceptionType": "Validation",
"title": { "message": "The request is not valid. Every problem is listed in messages.", "messageParameters": {} },
"messages": [
{ "message": "The value 'abc' is not valid for WaitSeconds.", "messageParameters": { "field": "waitSeconds" } }
],
"url": null
}GET /api/logos/{logoGuid}/file returns 204 No Content when the requested file type doesn't exist for that logo.
Keep your REST API key safe
A REST API key gives access to every print job, logo, proofreading and print file of your TronDesigner instance.
- Server only. Never put a REST API key into HTML, browser JavaScript, mobile apps or client-side logs. If it shows up in the browser's Network tab, treat it as leaked.
- Don't mix key types. The website integration key in
data-td-api-keyis public by design and protected by its allowed domains. The REST API key is a secret; keep it in a secret store or server environment variable, not in your repository. - Least privilege. Give read-only consumers Read Access only, and one key per system so you can revoke them independently.
- Restrict by IP when your servers have fixed outbound addresses.
- If a key leaks: disable or regenerate it in the Admin Portal, deploy the new value, check View Usage for calls you don't recognize, and contact PromoTron support if you see data you didn't create.
See also
- Webhooks — get notified when print jobs, proofreadings or print data are created
- Integration via URL — open the editor from a link
- Integration recipes — webhook + REST follow-up recipe
- Swagger reference