Skip to content
Connecting your systems

Using the HTTP API

Send artwork and collect the finished PDF from your own system, without a browser open. A key from Settings, five calls, and the same tracing the screens use.

Back to the help centre 6 min read

The API does what uploading a file on the Trace screen does: it starts a job, traces it with your print defaults, and hands back the same print-ready PDF. It is for a customer system or a shop script that uploads artwork and waits for the file, rather than a person at a screen.

Every address below starts with https://cutline.michaelagbozo.com/api/v1.

Getting a key

Make one under Settings → API tokens. It is shown once, when you make it, so copy it then. Send it on every request as a bearer token:

Authorization: Bearer cutl_your_key_here

Each key is given one or both of two permissions when it is made:

PermissionWhat it reaches
jobs:readYour account, your projects, your jobs and their proof pictures.
jobs:writeStarting a job, a shape or a batch, re-tracing one, and downloading the finished PDF or a batch's zip.

Downloading needs jobs:write even though it only fetches a file: the first download marks the job as downloaded and counts it, exactly as it does on the screens.

A key is the account. Anything it does counts as you doing it, so give one out no more widely than you would the password, and delete it from Settings the moment it is not needed.

Limits

The same limits the screens have: up to 6 jobs waiting or tracing at once, and up to 60 new jobs an hour. On top of that, 120 requests a minute per account. Going over any of them answers 429 with a sentence saying which one, and nothing is lost — try again shortly.

Starting a job

Send the artwork as a multipart upload. PDF, PNG, JPEG, WebP or TIFF, up to 90 MB and 20 megapixels. Give the finished height or width in centimetres, or leave both out and the size is read from the filename the way the screens read it.

curl -sS -X POST https://cutline.michaelagbozo.com/api/v1/jobs \
  -H "Authorization: Bearer cutl_your_key_here" \
  -H "Idempotency-Key: leaf-reprint-01" \
  -F "artwork=@LEAF_30CM.png" \
  -F "height_cm=30" \
  -F "parameters[alpha_cut]=240"

It answers 202 with the job, including its id and status. Send an Idempotency-Key and a retried upload returns the job the first one made, instead of starting a second.

FieldWhat it is
artworkRequired. The file.
height_cm, width_cmOptional, 1–1000 cm. One or the other — the artwork decides the second.
parameters[…]Optional settings, laid over your print defaults. The table further down lists them.
project_idOptional. One of your own projects.

Shapes

A shape is a cut drawn around the artwork rather than traced from it — a rectangle, rounded corners, an arch, a circle or an ellipse, with folds or a scalloped edge if you want them. It is what the Shape screen makes, and it has its own address because its settings are not the trace settings.

curl -sS -X POST https://cutline.michaelagbozo.com/api/v1/shapes \
  -H "Authorization: Bearer cutl_your_key_here" \
  -F "artwork=@BANNER_200h.pdf" \
  -F "parameters[shape]=rounded" \
  -F "parameters[radius_cm]=5"

What comes back is a job like any other: wait for it, read it and download it through the job addresses below. To change its numbers afterwards, post them to /shapes/JOB_ID/retrace — anything you leave out stays as it was.

Batches

Up to 40 files in one request, sent as artwork[]. Each file becomes its own job, sized from its own filename and given its own copy of your print defaults, exactly as a drop on the batch screen is. There are no settings and no size fields for the whole batch — files in one drop need different tuning, so re-trace any one of them afterwards. quantities[] says how many pieces each file was ordered, by position.

curl -sS -X POST https://cutline.michaelagbozo.com/api/v1/batches \
  -H "Authorization: Bearer cutl_your_key_here" \
  -F "artwork[]=@LEAF_30CM.png" \
  -F "artwork[]=@SIGN_250b.pdf" \
  -F "quantities[1]=25"

Each file is answered on its own: one that cannot start says why, and the rest start anyway. Ask for /batches/BATCH_ID until it reads finished, then collect every ready PDF as one zip from /batches/BATCH_ID/download. The zip carries a manifest.csv with each file's quantity and page size.

Waiting for it

A trace takes five to forty seconds, so ask for the job every few seconds until its status is no longer queued or tracing — or have it sent to you instead, with a webhook (below).

curl -sS https://cutline.michaelagbozo.com/api/v1/jobs/JOB_ID \
  -H "Authorization: Bearer cutl_your_key_here"

When it reads ready, the report on the job carries the same figures as the preflight on the Trace screen — page and artwork size, paths and how many close, and the effective resolution. Read them before you print, as you would on the screen.

Collecting the file

curl -sS -o LEAF_32h_21b.pdf https://cutline.michaelagbozo.com/api/v1/jobs/JOB_ID/download \
  -H "Authorization: Bearer cutl_your_key_here"

Before the job is ready this answers 409. Files are kept seven days, the same as on Recent; after that it answers 404.

Being told instead of asking

Under Settings → Webhooks, add a URL of your own and pick what it hears about. Each job and batch is then posted to it, signed, as it lands. Traced files, shapes and panels all send the same job events.

EventWhen
job.readyA job finished and its PDF is ready.
job.failedA job could not be cut. The job says why.
job.needs_sizeA job stopped because nothing said how big to cut it. Sent instead of job.failed.
batch.finishedEvery file in a batch has stopped moving.

The body is JSON: an id, the type, when it happened, and data — which is exactly what asking for that job, or that batch, would have answered at that moment. Each request carries a Cutline-Delivery header that stays the same on every retry, so you can ignore one you have already handled.

Checking the signature

Every endpoint has a signing secret, shown once when you add it. Each request carries a Cutline-Signature header of the form t=TIMESTAMP,v1=SIGNATURE, where the signature is an HMAC-SHA256, in hex, of the timestamp, a full stop and the raw request body, keyed with that secret. Refuse anything whose signature does not match, or whose timestamp is more than five minutes from your clock. Hash the body exactly as it arrived, not the JSON after you have parsed it.

$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_CUTLINE_SIGNATURE'] ?? '';
$secret = getenv('CUTLINE_WEBHOOK_SECRET');

$parts = [];
foreach (explode(',', $header) as $item) {
    [$key, $value] = explode('=', $item, 2);
    $parts[$key][] = $value;
}

$timestamp = (int) ($parts['t'][0] ?? 0);
$expected = hash_hmac('sha256', $timestamp.'.'.$payload, $secret);

$genuine = abs(time() - $timestamp) <= 300
    && array_filter($parts['v1'] ?? [], fn ($v) => hash_equals($expected, $v)) !== [];

http_response_code($genuine ? 204 : 400);

For a day after you make a new secret, each request carries two signatures, one for each key. Accept it if either matches, and swap the key on your side whenever suits you inside that day.

Answering

Answer with any 2xx within ten seconds, and do the work afterwards. Redirects are not followed. Anything else is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then given up; every attempt is in the endpoint's delivery log. An endpoint still failing after a day of that is switched off and you are emailed. Switch it back on and send a test once it is fixed.

Webhooks go only to https addresses on the public internet. A URL that points, or later starts pointing, at a private, loopback or link-local address is refused — checked when you save it and again before every request.

Every call

CallPermissionWhat it does
GET /mejobs:readWho the key belongs to, what it may do, and your limits.
GET /projectsjobs:readYour projects, 25 to a page.
GET /clientsjobs:readYour clients and their machine profiles, 50 to a page. Pass a client_id when you start a job and it starts from that profile.
GET /jobsjobs:readYour jobs, 25 to a page. Filter with status, project_id, batch_id or since (a date and time).
GET /jobs/{id}jobs:readOne job as it stands now.
GET /jobs/{id}/proofjobs:readThe proof picture, as a PNG.
POST /jobsjobs:writeStart a job.
POST /jobs/{id}/retracejobs:writeTrace the same upload again with new settings. A shape job re-runs through /shapes/{id}/retrace instead.
GET /jobs/{id}/downloadjobs:writeThe finished PDF, named NAME_Hh_Wb.pdf.
POST /shapesjobs:writeStart a shape job.
POST /shapes/{id}/retracejobs:writeDraw a shape job again with new numbers.
POST /batchesjobs:writeStart up to 40 files at once.
GET /batches/{id}jobs:readEvery job in a batch, and whether it has finished.
GET /batches/{id}/downloadjobs:writeEvery finished PDF in the batch as one zip, once the batch has finished.

Settings you can send

Each one is optional and is laid over your print defaults, so leave out anything you have already settled. Sizes are centimetres; tolerances and offsets are millimetres.

SettingDefaultOn the screen
alpha_cut128Alpha cut, 0–255
min_area0.02Minimum area, a fraction of the largest piece
open_mm0.8Opening, mm
relief_mm0Relief, mm
threshold16Background tolerance, used only without transparency
crop_bottom0Cut off the bottom, a fraction of the height
smooth_mm0.5Edge smoothing, mm
fit_error_mm0.25Curve fit tolerance, mm
offset_mm0Cut offset, mm. Negative pulls the cut inside the print.
clearance_cm2Page clearance, cm
stroke_pt3Stroke weight, pt
cut_holestrueGaps: cut the gaps the artwork encloses
hole_min_area_cm22Smallest gap to cut, cm²
hole_min_web_mm3Thinnest material to leave around a gap, mm
mirroredfalseFlip, for second-surface work. Left to right only.
weeding_gap_mm0Weeding box gap, mm. 0 is no box.
weeding_per_piecefalseOne weeding box per separate piece
folds0Fold lines, 0–40
fold_dirverticalvertical or horizontal
fold_spacing_cm0Gap between folds, cm. 0 spreads them evenly.
fold_inset_mm3Stop short of the cut, mm
fold_stroke_pt3Score weight, pt
on_existing_cutfailfail refuses artwork that already carries a cut path; ignore traces it anyway
on_printed_sizefailfail stops when a size printed on the artwork disagrees with the job; ignore traces at the job's size

Note that min_area is a fraction and hole_min_area_cm2 is square centimetres. They are two different measurements on the same job.

When something is refused

StatusMeaning
401No key, or a key that is not valid. Being signed in on the site does not count.
403The key lacks the permission the call needs, or the account's email is not verified yet.
404No such job — including a job that belongs to another account.
409Downloading before the job is ready, a batch zip before the batch has finished, or re-tracing through the other kind of job's address.
422A setting out of range, no size anywhere, or artwork over 20 megapixels. The message says which.
429Over one of the limits above.

Not in the API yet

  • Panelling a piece wider than the press. Panelled jobs can be read and downloaded, not started or re-run.
  • Adding or changing webhook endpoints. They are set up under Settings → Webhooks; the API only sends to them.
  • Sending proofs to customers, and cancelling or deleting a job.

The rules that apply on the screens apply to work sent through a key — see the acceptable use policy.

Building against it?

Tell us what you are connecting and we will help you get it right.