preflight.art API

Error Codes

Every API error returns a JSON body with error (human-readable) and code (machine-readable). Match against code in your retry / alerting logic — error messages may change wording over time; codes are stable.

CodeHTTPWhat it meansHow to fix
API_KEY_REQUIRED 401 Missing, malformed, or unrecognised x-api-key header. Verify the key is exactly as issued (no extra whitespace), being sent as x-api-key (not Authorization), and not revoked.
V1_NOT_AVAILABLE_ON_THIS_PLAN 403 Your account plan does not include v1 API access. Contact hello@preflight.art to request enterprise access.
IP_NOT_ALLOWED 403 This API key has an IP allowlist and your request originated from outside it. Add your egress IP to the key allowlist via /account/api-keys → Edit, or remove the allowlist.
INSUFFICIENT_SCOPE 403 Key is restricted to specific scopes and the called endpoint requires a different one. Issue a new key with the needed scope (preflight:read / preflight:write / preflight:delete), or use an unrestricted key.
FILE_MISSING 400 POST /v1/preflight received neither a file field nor a source_url. Send the upload as multipart/form-data with a `file` field, or send a `source_url` download link (form field or JSON).
SOURCE_URL_INVALID 400 source_url is empty, too long, not a URL, not https, or carries a user name or password. Send a full https:// download link.
SOURCE_URL_BLOCKED 400 source_url points at a private, local or reserved network address. Use a link on the public internet.
SOURCE_URL_FETCH_FAILED 400 The link could not be downloaded: host not found, connection refused, a non-2xx answer, too many redirects, or an empty file. Open the link in a private browser window. It must download the file with no login. Send a public or pre-signed link.
SOURCE_URL_NOT_A_FILE 400 The link returned a web page instead of a file. Use the direct download link, not the preview or share page.
SOURCE_URL_UNSUPPORTED_TYPE 400 The downloaded file is not a PDF, .ai, .eps, .png, .jpg or .zip. Export the artwork as PDF, or pass source_filename with the right extension.
SOURCE_URL_TOO_LARGE 413 The file at the link is over the upload limit. Reduce the file (flatten, downsample images) and try again.
SOURCE_URL_TIMEOUT 400 The link stopped responding, or the download took over 4 minutes. Try again; if it keeps happening, upload the file directly.
FILE_AND_SOURCE_URL 400 The request carried both a file and a source_url. Send one or the other.
JOB_NOT_FOUND 404 No job with that id is accessible to your account. Double-check the id. A deleted job still returns status=deleted on GET /status, but its report/pdf/source return 404.
NOT_READY 409 You called /report or /pdf before the job finished. Poll GET /v1/preflight/{id}/status until status is complete.
REPORT_REBUILDING 503 The stored PDF for this job came from a report design we have retired, and rebuilding it in the current design did not finish. Retry the download in a minute. If it keeps happening, re-run the file.
ANALYSIS_FAILED 422 The engine could not analyze this file. Check the error message; usually a corrupted PDF, password-protected file, or unsupported format. Re-export and resubmit.
INVALID_PDF 400 Upload header doesnt start with %PDF-. Confirm the file is a real PDF. .ai/.eps with embedded PDF should work too.
PAYLOAD_TOO_LARGE 413 Uploaded file exceeds the 500 MB limit (or a smaller serverless body cap if reached via a proxy). Send the file to the Railway endpoint directly, or reduce the file size (flatten, downsample images), and re-upload.
INVALID_PROFILE 400 Unknown profile id passed in form data. Use one of: digital-labels, flexo-labels, flexpack, cartons-folding. Check GET /v1/profiles.
INVALID_ORDER_SPEC 400 order_spec was not valid JSON, or a field was out of range — a size outside 0.25"–60", a width without a height, or a non-boolean backprinted. Send inches (not millimetres), send width_in and height_in together, and send backprinted as true or false. The error message names the field.
RATE_LIMIT_HOURLY 429 You exceeded your hourly request cap on this key. Wait the seconds returned in Retry-After. Contact support to raise the cap if this is recurring.
RATE_LIMIT_DAILY 429 You exceeded your daily request cap on this key. Wait until daily reset, or contact support.
API_KEY_REJECTION_RATE_LIMIT 429 Too many bad-key attempts from your IP (anti brute-force). Stop sending bad keys. Wait 15 minutes. Verify the secret store your code reads from has the right value.
NO_WEBHOOK_URL 400 You called /webhooks/test with no URL configured on the key and no body override. Edit the key to set a webhook URL, or pass {"url": "https://..."} in the body.
NO_WEBHOOK_SECRET 400 The key was issued without a webhook URL, so no signing secret exists. Re-issue the key with a webhook URL, or rotate it and add one.
INVALID_WEBHOOK_URL 400 Override URL must be https://. Always use https:// — we never deliver to http:// targets (SSRF + plaintext risk).
EVENT_NOT_FOUND 404 event_id query param doesnt match any delivery for this key. Verify the event id; only events from this key are visible.
INVALID_EVENT_ID 400 event_id must be an integer. Pass the numeric event_id returned by POST /v1/webhooks/test.
ENQUEUE_FAILED 500 Internal error while queuing the webhook. Retry once. If it persists, contact hello@preflight.art with the timestamp.
REFERENCE_TOO_LONG 400 `reference` is longer than 128 characters. Send your own short id for the work (a quote or PO number). It is refused rather than cut short, so a result is never written back to the wrong record.
INVALID_REFERENCE 400 `reference` is not a string, or contains control characters. Send plain text.
REFERENCE_REQUIRED 400 POST /v1/intake-links was called without a reference. Every intake link belongs to a quote or job: send `reference`, or send `quote_request_id` to get one link per quote request.
QUOTE_REQUEST_ID_REQUIRED 422 POST /api/v1/upload-links was called without `quote_request_id`. Send the id of the quote request the link collects artwork for. The same id always returns the same link.
INVALID_QUOTE_REQUEST_ID 422 `quote_request_id` is not 1 to 128 printable characters. Send your quote request id as plain text or a number.
INVALID_FIELD 422 A link field (`qre`, `project_name`, `company`, `rep_name`, `rep_email`, `label`, `customer_name`, `job_description`) is not text, is too long, or `rep_email` is not an email address. The response names the `field`. Send plain text, or null to clear it.
INVALID_JSON 422 The body of POST /api/v1/upload-links is not JSON. Send `Content-Type: application/json` and a JSON object.
INTAKE_TOKEN_SECRET_MISSING 503 This server is not set up yet for links by quote request, or for the general upload page. Contact hello@preflight.art.
METADATA_TOO_LARGE 400 `metadata` is over 2 KB as JSON, or has more than 32 keys. Keep metadata to a few short labels; it is echoed on every webhook.
INVALID_METADATA 400 `metadata` is not a flat JSON object of strings, numbers or booleans. Send one level of key/value pairs. Nested objects and arrays are not stored.
INVALID_DEPTH 400 An intake link `depth` other than quick or full. Use "quick" (the default) or "full".
INVALID_EXPIRY 400 `expires_in_days` is not a number above 0 and at most 3650. Omit it for a link that does not expire.
INVALID_MAX_UPLOADS 400 `max_uploads` is not a whole number from 1 to 100000. Omit it for unlimited uploads.
INTAKE_LINK_NOT_FOUND 404 No intake link with that id or token is visible to this key. Links are private to the key that made them.
INTAKE_LINK_REVOKED 410 The link was revoked, or the API key that made it was. Make a new link for the quote.
INTAKE_LINK_EXPIRED 410 The link passed its `expires_in_days`. Make a new link for the quote.
UPLOAD_LIMIT 410 The link has taken its `max_uploads`. Make a new link, or create links without a cap.
NO_ARTWORK 400 Nothing in a drop-page upload could be checked. The response lists each file and why. Upload a PDF, Illustrator (.ai) or EPS file, or a .zip of them.
TOO_MANY_FILES 400 More than 25 files in one drop-page upload. Upload in smaller groups, or zip them (up to 25 artworks per upload).
FILE_TOO_LARGE 413 A drop-page file is over the 500 MB limit. Reduce the file (flatten, downsample images) and upload again.
ENCRYPTED_ZIP 400 The zip is password protected. Remove the password and upload again.
INVALID_PACKAGE_ZIP 400 The zip could not be read, or has no .ai, .pdf or .eps inside. The message says what the zip contained. Zip the artwork itself.
ZIP_TOO_LARGE 400 The artwork in the zip adds up to more than twice the upload limit unpacked, the zip unpacks to more than 5 GB, or it unpacks to more than 100 times its own size. Split it into smaller zips, or zip the files again.
SUBMISSION_TOO_LARGE 413 One drop-page upload is more than 2 GB in all. Upload the files in smaller groups.
BAD_SIGNATURE 403 A /v1/dl download link was altered or was not issued by us. Use the link exactly as delivered, or mint a new one with GET /v1/preflight/{id}/links.
LINK_EXPIRED 410 A /v1/dl download link is past its expiry. Mint a new one with GET /v1/preflight/{id}/links (up to 7 days).
KEY_REVOKED 403 The API key a download link was issued for has been revoked. Links die with their key. Mint new ones with a current key.
FILE_NOT_FOUND 404 The requested file is gone (past its retention) or was never produced (no report PDF). Pull files into your own archive when the webhook arrives.
SUBMISSION_NOT_FOUND 404 No submission (upload) with that id is visible to this key. Use the upload_id from the upload, upload.completed or submission.ready. Submissions are private to the key they were made on.
SUBMISSION_NOT_READY 409 POST /v1/submissions/{id}/resend was called while its files are still being checked. Wait: submission.ready is sent by itself when every file has finished.
EVENT_NOT_SUBSCRIBED 409 A resend of submission.ready on a key that is not subscribed to it. Add submission.ready to the key's webhook events on /account/api-keys. It is never sent to a key that has not asked for it.
SEND_IN_PROGRESS 409 The last send of this submission is being delivered at this moment. Try again in a few seconds. Only one send of a submission is ever in flight.
SUBMISSION_READ_FAILED 500 Internal error while reading a submission. Retry once. If it persists, contact hello@preflight.art with the timestamp.
LINK_NOT_FOUND 404 A /v1/f or /v1/r link that we did not issue, or a file link opened on the report path (or the other way round). Use the link exactly as delivered.
LINK_REVOKED 410 The links to this file were turned off with DELETE /v1/files/{id}/links. Get new ones with GET /v1/files/{id}/links.
FILE_EXPIRED 410 The original file has passed its key's retention period and is no longer stored. Pull files into your own archive when the webhook arrives. The report stays readable.
NOT_ARTWORK 422 ziflow-source was asked for a reference file (an image, .psd, .docx or .xlsx kept with a submission), not artwork. Ask for an artwork file. Reference files have a file_link, which opens the original.
FILE_LINKS_UNAVAILABLE 503 This server is not set up yet for links that do not expire. Contact hello@preflight.art.
SUBMITTER_REQUIRED 400 An upload on the general page without the sender's name, email or company. The response names the `field`. The page asks for all three before it uploads.
INVALID_EMAIL 400 The email typed on the general page does not look like an email address. Check it and upload again. `field` is `email`.
FILE_NAME_REQUIRED 400 A file was started (POST /v1/intake/{token}/parts) without `file_name`. Send the file's name and its size in bytes.
INVALID_SIZE 400 A file was started without its size in bytes. Send `size` as a whole number of bytes.
UNSUPPORTED_TYPE 422 A file was started whose type the drop page would only skip (an .indd, say). Nothing was sent. Export it as PDF and upload that.
TOO_MANY_PARTS 429 More than 100 files are on their way up on one link at once. Finish those first; unfinished ones are removed after a day.
PART_NOT_FOUND 404 That file is not on its way up on this link: never started, already handed over, or removed after a day with no piece. Start it again.
OFFSET_MISMATCH 409 A piece did not start where the file stops (a piece sent twice, or one skipped). `received` says where it stops. Send the next piece from there.
PART_BUSY 409 A piece of this file is being written at this moment. Wait a moment, ask for `received`, and carry on from there.
PART_OVERFLOW 413 The piece goes past the size the file was started with. Send only the rest of the file.
CHUNK_TOO_LARGE 413 A piece over 16 MB, or longer than its Content-Length. Send pieces of `chunk_bytes` (8 MB).
LENGTH_REQUIRED 411 A piece was sent without a Content-Length. Send each piece as a plain body with its length.
PART_INCOMPLETE 409 An upload named a file that has not finished arriving. Nothing was taken. `part_id` and `received` say which and how far. Finish it and hand over again.
PART_WRITE_FAILED 500 A piece could not be stored. Send it again. If it persists, contact hello@preflight.art.
FILE_NOT_IN_SUBMISSION 404 `replaces_file_id` is not a file in this submission. Use a file_id from the submission's files (GET /v1/submissions/{id}).
FILE_ALREADY_REPLACED 409 That file was already replaced by a corrected one. `replaced_by` names the newer file: replace that one instead.
NOT_A_LINK_SUBMISSION 409 Corrected files can be added to uploads on an intake link only. Send the corrected file with POST /v1/preflight instead.
ZIP_NOT_ACCEPTED 422 A corrected file was sent as a zip. Send the file itself (a PDF, .ai or .eps, or a reference file).
INVALID_FILE 422 The corrected file's bytes do not match its name (a .pdf that is not a PDF, say), or its type is not one we take. The message says what to send.
CORRECTION_FAILED 500 The corrected file could not be added. The submission is as it was. Retry once. If it persists, contact hello@preflight.art.

Webhook delivery headers

Two response headers worth wiring into your monitoring: