Skip to content
Docs
Sign in

Estimating API

Create an estimate with its graphic, materials or parts in one call, send a designer link, poll for the result and pull the quote, data export and cut files.

For integrators

Drive an estimate from your own system, for example a web shop:

  1. Create the estimate with the customer's graphic, the materials they may choose and a designer link.
  2. Send your customer to the designer link, where they draw the parts on their graphic.
  3. Poll until they click Finish.
  4. Pull everything they drew, with the quote, nests and cut-file links.

All calls are team-scoped under /a/<team_slug>/estimating/api/v1/ and need an API key (see the API overview). The examples use this shell setup:

export HOST=https://app.example.com
export AUTH="Authorization: Api-Key $API_KEY"

Create an estimate in one call#

POST /a/{team_slug}/estimating/api/v1/estimates/

Create an estimate in one call: fields, optional graphic (`file`), designer materials (`material_ids`, `default_material`) and a designer link (`designer_link`: true or options).

estimating_estimates_create · View in API reference

Send multipart/form-data (needed to include a file) or JSON. You need either name or file; everything else is optional.

curl -sS -X POST "$HOST/a/acme/estimating/api/v1/estimates/" -H "$AUTH" \
  -F "file=@postcard-a.pdf;type=application/pdf" \
  -F "external_id=order-1001" \
  -F "customer_name=C-102" \
  -F "reference=PO-5555" \
  -F "material_ids=7" -F "material_ids=8" \
  -F "default_material=7" \
  -F 'metadata={"order_id": 1001, "source": "webshop"}' \
  -F 'designer_link={"role": "customer", "label": "Proof for C-102", "return_url": "https://example.com/orders/1001/done", "expires_in_days": 14}'
Field Notes
name Up to 200 characters. Defaults to the file name, without its extension, when you send a file.
customer_name, reference, notes Free text (customer up to 200, reference up to 100 characters).
status draft (default), sent, won or lost.
unit_system imperial (default) or metric: how the app shows lengths and areas.
external_id Your own id, up to 100 characters, unique in the team. A duplicate gets 409 with existing_id.
metadata Any JSON object up to 16 KB, returned as sent. In multipart, send it as a JSON string.
file The graphic: PDF, PNG, JPG or WebP, up to 50 MB (images up to about 89 megapixels). It is also saved in Files, in the estimate's folder under Estimates.
material_ids The materials offered in the designer, up to 500. Repeat the field in multipart, or send a JSON array. Empty or left out: every active material of the team.
default_material The material the designer starts with. It must be in material_ids when that list isn't empty.
designer_link true for a link with default options, or an object of link options. Leave it out for no link.
parts Up to 500 parts sized by hand or priced by hand, for a quote without artwork. In multipart, send a JSON array string.
price With parts: true prices every group before answering (within 10 seconds).

The create is all or nothing: if any field or part fails, nothing is stored. When your team's storage is full the graphic is refused with 507 and the code quota_exceeded (see Storage), and no estimate is kept. Part errors come back by position: {"parts": {"1": {"size": ["..."]}}}. The response (201) is the estimate. Besides the fields above it has id, revision, totals (part_count, total_area_sq_in, total_cost, total_price), documents (with their download URLs and page sizes), designer_links, designer_url (the newest active link, or null), designer_submitted_at and data_url. With parts it also has parts (as the parts endpoints return them) and quote.

A quote without artwork, in one call:

curl -sS -X POST "$HOST/a/acme/estimating/api/v1/estimates/" -H "$AUTH" -H "Content-Type: application/json" -d '{
  "name": "Spring promo", "customer_name": "Larkfold Studio", "price": true,
  "parts": [
    {"name": "Postcard A", "size": "4x6", "quantity": 5000, "alt_quantities": [10000], "material": 41,
     "route": "offset", "inks": "4/4", "external_id": "L-1"},
    {"name": "Event poster", "width": 24, "height": 36, "unit": "in", "quantity": 10, "material": 7},
    {"name": "Design fee", "route": "custom", "price_each": "75.00"}
  ]}'

The same in separate calls. Create with JSON, then add the file, set the materials and create a link:

POST /a/{team_slug}/estimating/api/v1/estimates/{id}/documents/

Add a PDF or image to the estimate (same validation as the upload page).

estimating_estimates_add_document · View in API reference

From Files. To add a file your team already has in Files, send its version's id (version_id, from the Files API) instead of uploading it again. The document shows that version: its bytes aren't copied, and a newer version of the file doesn't change the estimate. The same type and size rules apply (400 on version_id); a version that isn't in your team's library, or whose file is in the trash, gets 404. Designer links can't add files this way.

POST /a/{team_slug}/estimating/api/v1/estimates/{id}/documents/from-files/

Add a version from the team's Files library to the estimate: a PDF, PNG, JPG or WebP up to 50 MB.

estimating_estimates_add_document_from_files · View in API reference

Document downloads. A document's url and preview_url point at this API, never at file storage: fetch them with the same Authorization header (or a signed-in session). The bytes come back as uploaded, under the original file name; the preview is the first page as an image. Designer link pages get the same files through the link.

GET /a/{team_slug}/estimating/api/v1/documents/{id}/file/

The document's bytes (the PDF or image as uploaded), inline under its original file name.

estimating_documents_file · View in API reference

GET /a/{team_slug}/estimating/api/v1/documents/{id}/preview/

The document's first-page preview image (404 when it has none).

estimating_documents_preview · View in API reference

PUT /a/{team_slug}/estimating/api/v1/estimates/{id}/materials/

Replace the designer's material allowlist ([] = all active materials) and default material.

estimating_estimates_set_materials · View in API reference

PATCH /a/{team_slug}/estimating/api/v1/estimates/{id}/

Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.

estimating_estimates_partial_update · View in API reference

PATCH changes the header fields, external_id, metadata, material_ids and default_material. It refuses file and designer_link, which only work on create. PUT on an estimate answers 405.

Designer links for customers and staff explains links from the app's side. A designer link is an unguessable URL (https://app.example.com/estimating/d/<token>/) that opens this one estimate in the Takeoff designer, with no sign-in. The token in the URL is the credential: treat the link like a password and revoke it when you're done.

POST /a/{team_slug}/estimating/api/v1/estimates/{id}/designer-links/

Create a designer link: an unguessable URL that opens this estimate's designer without login.

estimating_estimates_designer_links_create · View in API reference

Option Default Meaning
label empty Your note, up to 120 characters.
role customer Customer or Staff, see below.
show_prices true Customer links: show sell prices. Off: measurements only.
allow_uploads true Customer links: let them add more files.
allow_parts false Customer links: let them add parts by size (see below).
return_url empty Where Finish sends the browser. http or https only.
expires_in_days or expires_at never 1 to 365 days from now, or a future date and time. Send one, not both.

What each role can do. The server enforces this, not only the page.

Customer Staff
Draw, measure, set scales yes yes
Pick materials from material_ids from material_ids, and create new ones
Sell prices when show_prices is on yes
Costs, margins, machine rates never (removed from every response) yes
Production settings, cut files, optimising a nest no (403) yes
Edit the header (name, customer, status…) no (403) yes
Add files when allow_uploads is on yes
Add parts by size when allow_parts is on: name, shape, size, quantity, material and inks only yes
Add boxes, booklets or labels no (400) yes
Change a part your team set up its quantity only (403 for anything else) yes
A box's plan, machines, steps and notices; the cost of dies and plates never yes

A customer can edit and delete the parts they added. Their saves never remove a part your team added by size or a price-only line, and never change its costs, hand prices, finishing or quantities to quote. Price-only lines can't be added from a customer link (400). A customer sees a box line read-only, with its dies and plates as one-off rows priced when show_prices is on; its production group (part-<uuid>) answers 404 on a customer link.

Manage links later with these:

GET /a/{team_slug}/estimating/api/v1/estimates/{id}/designer-links/

The estimate's designer links, newest first (revoked and expired ones included).

estimating_estimates_designer_links_list · View in API reference

GET /a/{team_slug}/estimating/api/v1/designer-links/{token}/

estimating_designer_links_retrieve · View in API reference

PATCH /a/{team_slug}/estimating/api/v1/designer-links/{token}/

estimating_designer_links_partial_update · View in API reference

DELETE /a/{team_slug}/estimating/api/v1/designer-links/{token}/

Revoke the link (sets revoked_at); its URL then answers 410 Gone.

estimating_designer_links_revoke · View in API reference

A revoked or expired link answers 410 Gone, and its page tells the visitor the link is no longer available. Whatever they saved is kept.

Estimate lines#

An estimate's parts are its lines. A part is drawn on a page of the graphic, sized by hand (no graphic needed), a box sized by its dieline, or a price-only item (a design fee, outsourced work). Every part has a quantity, up to 3 other quantities to quote, a material, a route (roll for wide format, sheet for digital sheets, offset, blank for made-to-size board, or custom for a price you type), inks and finishing.

POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/

Add one sized part or price-only item, at the end of the list.

estimating_estimates_parts_create · View in API reference

Field Notes
name, notes Free text.
size The size as you would type it: 4x6, 24 x 36 in, 8 1/2 x 11, A4, 3 oval, 3.5x2 r0.125. Or send width and height with unit (in, mm, cm, ft or m; the estimate's own unit when left out), or width_in and height_in.
shape rect, rounded (with corner_radius), oval, custom (with points: 3 to 500 [x, y] points in inches), or dieline for a box (see Boxes).
dieline Boxes only: the definition (style, L, W, D, unit, basis, grain, params).
die_design Boxes only, instead of dieline: the id of a single-up dieline of your tool library (a CAD dieline box); null turns it back into a box drawn from a dieline.
quantity, alt_quantities 1 to 1,000,000. Up to 3 other quantities, each different: quoted separately, never counted in the total.
material or material_sku A material id, or the exact SKU of an active material.
route roll, sheet, offset, blank or custom. Left out: the material's (a roll material runs on roll, a sheet stock on sheet, or offset when it runs on offset only; a box on a made-to-size grade runs on blank).
inks 4/0, 4/4, 4/1, 1/0, 4+PMS 185 C/1 or none. On sheet and offset an empty value becomes 4/0.
finishing [{"id": 5, "count": null}]: operations from the finishing library. count is per piece for operations priced each; folding operations take a style (half, letter, z, gate, double_parallel, roll, accordion_4). An archived or unknown operation is left off, with a warnings entry.
bleed In unit; left out: the group's.
cost_each, price_each Decimal strings. On custom the price each is required to price the line. On any other route price_each overrides the computed price, and also prices a line that can't be computed yet.
product, external_id The product the part came from; your own line id (up to 100 characters).
family The part's product family: carton or corrugated on boxes, set by the server from the dieline style (a value you send on a box is ignored). Booklets and labels come in a later release; until then a family on any other part is 400.
spec The family's options (see Boxes). Left out: kept; null: reset. An unknown or refused option comes back as {"spec": {"convert": ["..."]}}; options on a part with no family are 400.

A custom part sent without a size is a price-only item ("kind": "item" says the same). Parts come back with id, source (drawn, manual or item), description, the size in the estimate's unit and in inches, area, perimeter, sides, family, spec, plan (a family part priced on its own: ups, press sheet, machines; null otherwise), tooling (its one-off dies and plates: key, label, total_cost, total_price, billing, tool) and price (total, each, tooling, pending, blocked; tooling is the one-off tooling billed on its own row, not in total). Sizes of drawn parts come from the drawing: changing one gets 400 (sending a part's own values back, as GET gave them, is fine).

Boxes#

A box is a part with "shape": "dieline" and a dieline: its outline, size, area and perimeter come from the dieline, so size and points are refused on it (400), and so are width and height (or width_in, height_in) unless they equal the box's blank: a box's own part, as GET gave it, can be sent back. The board follows the part's material: the server stores the material's catalogue board, caliper and allowances in the dieline, and keeps them until you update the box. A caliper_mm you send in the dieline is the box's own caliper: it wins over the material's, also when the material changes, until you update the box. A box without all three sizes is saved and waits for them, like a part with no size.

{"name": "Shipping box", "shape": "dieline", "material_sku": "C32-MTS", "route": "blank", "quantity": 1000,
 "dieline": {"style": "fefco_0201", "L": 12, "W": 10, "D": 8, "unit": "in"}}
  • Routes: sheet (printed on sheets, cut on the cutting table, ganged with the flat parts on the same stock), offset (die-cut cartons, each priced on its own: press, die-cutter, folder-gluer and a die from your rate card; and litho-laminated corrugated: a corrugated grade on offset is laminated to an offset-printed sheet, with spec.litho_material the sheet stock and spec.litho_inks its inks, and needs a laminator), blank (board made to size: converted on a flexo folder-gluer or die-cut, or priced per blank by its rectangle: ft², m² or 1,000 ft²) and custom. A box on roll gets 400. A flat part on blank gets 400.
  • family is carton or corrugated, from the style. spec holds the costing options: convert (how the box is made: die for die-cut and folder-gluer, table for the cutting table, ffg for a flexo folder-gluer, none for board only, litho_lam for litho-laminated; the route's default when left out), versions (artworks sharing one die, 1 to 50), tooling_billing (separate, the default: dies and plates on their own one-off row; included: in the line's price) and, on corrugated, print_panels (1, 2 or 4) and joint (glued). A carton on offset or with convert: die, and corrugated converted on blank, is priced on its own in production group part-<uuid>.
  • The die (die-cut and litho-laminated boxes, see Die on hand or a new die): die_policy is prefer_on_hand (a die on hand that fits, else a new die; the team's default when left out), compare (the least all-in), standing (the die in tool, else the cheapest one on hand) or new; tool is the id of a die of your library to use (on hand; with die_policy: standing also one at the die maker or in repair); die_rush (true/false) adds the rate card's rush percentage to a new die; tool_accepted_near ({"tool", "kind", "dist_mm", "geometry_hash"}, null clears it) makes a near match count for this box while geometry_hash is the part's dieline_info.hash (it lapses when the box changes). An unknown policy, a die of another team or an archived one gets 400 on that key. A customer designer link's spec has none of tool, die_policy, die_rush and tool_accepted_near; its save keeps the stored ones, and one sent gets 403.
  • A size or option out of range comes back as {"dieline": {"L": ["..."]}}.
  • Parts come back with dieline, dieline_info (the blank's size and area, MSF per 1,000, outline, grain and line lengths, in inches) and downloads (one URL per file format).
  • Update. A box keeps the geometry it was quoted with. To redraw it with the current box generator and its material's current caliper and allowances, send its dieline without caliper_mm and allowances.

A box from a CAD dieline#

Send die_design (the id of a folding carton or corrugated single-up dieline in Tools & dies, active and your team's) instead of a dieline. The box takes its blank, rule lengths and family from it and never redraws it; its board still follows the material. dieline comes back null, dieline_info.style.id is imported, and the part adds die_design and die_design_summary (id, name, family, format, facts, file_version).

{"name": "Tuck box", "shape": "dieline", "die_design": 12, "material_sku": "SBS-18-2840", "route": "offset",
 "quantity": 10000}
  • Another team's, an archived or an unknown dieline, or one whose family is not folding carton or corrugated, gets 400 {"die_design": ["..."]}; a stock of the other family gets 400 on material. A dieline archived after the part took it stays on the part.
  • The single-up's facts give what the file can't: gluer (none, straight_line, crash_lock, four_corner, six_corner), glue_points, grain_axis. On a way of making that folds and glues (convert die, ffg or litho_lam) a box with no gluer class is blocked with imported_facts_missing; on the others it prices with the notice imported_gluer_unknown. The part's blank is rebuilt from the single-up's current facts the next time the part is saved.
  • downloads has pdf, dxf, cf2 (drawn from the stored single-up; no template, no fold check) and original (a redirect to the die file it was read from, when there is one).
  • Designer links never get die_design or die_design_summary, and customers can't pick one.
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/dieline/

Download a box part's dieline

estimating_estimates_parts_dieline · View in API reference

format is pdf (the dieline for the die maker or cutting table), template (for your designer), dxf, cf2 or svg; pdf when left out. A CAD dieline box takes pdf, dxf, cf2 or original (302 to the original file). It answers like the dieline export: the die files wait for the fold check (409 foldcheck_failed, 503 while it runs), the template and the SVG never do. A flat part, a box with no size yet, or a box that can't be drawn (its stock has no caliper, or its blank is too large; its downloads is null) gets 404.

curl -sS -H "$AUTH" -OJ "$HOST/a/acme/estimating/api/v1/estimates/42/parts/$PART/dieline/?format=cf2"
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/

Parts of an estimate: drawn, sized and price-only items.

estimating_estimates_parts_list · View in API reference

Filter with source, route or external_id.

GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/

Parts of an estimate: drawn, sized and price-only items.

estimating_estimates_parts_retrieve · View in API reference

PATCH /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/

Change any field; fields left out keep their value.

estimating_estimates_parts_partial_update · View in API reference

PATCH changes only the fields you send.

DELETE /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/

Parts of an estimate: drawn, sized and price-only items.

estimating_estimates_parts_destroy · View in API reference

POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/bulk/

Create, update and delete parts in one all-or-nothing call (at most 500 operations).

estimating_estimates_parts_bulk · View in API reference

One call creates, updates and deletes up to 500 parts, all or nothing: {"create": [...], "update": [{"id": "...", "quantity": 250}], "delete": ["..."]}. Errors come back by section, even for one operation: {"create": {"0": {...}}, "update": {"<id>": {...}}, "delete": {"<id>": [...]}}.

Concurrency. Every write bumps the estimate's revision and answers with ETag: "<revision>". Send If-Match: "<revision>" (or base_revision in the body) to get 409 with the current revision when someone changed the estimate since you read it.

The quote#

GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/quote/

The estimate's quote: lines, groups and totals, plus valid_until and currency.

estimating_estimates_quote · View in API reference

The quote has lines (one per part), groups (the parts sharing a route and a material) and the totals: price, cost, margin, rounding, minimum, pieces and pending_groups, plus valid_until and currency. On a line, total_price and total_cost are the all-in figures (material, printing, cutting, press, finishing and hand prices); cost and price keep their earlier meaning, the material alone. Groups priced on sheets or offset are worked out on the server: by default this call prices the ones without a fresh price first, within budget seconds (10, at most 20); any left have pending: true and no price yet. refresh=0 answers at once.

Products and finishing#

Products are saved parts with defaults (size, quantity, route, stock, inks and finishing), picked when adding a line. A starter set (postcard, flyer, business card, posters, banner, sticker, rigid sign, custom item, design fee) is created the first time the team's products are read; it carries no prices. DELETE archives a product.

POST /a/{team_slug}/estimating/api/v1/products/

Product templates: saved parts with defaults.

estimating_products_create · View in API reference

Send defaults with any of the part fields above in inches (width_in, height_in, corner_radius_in, bleed_in) plus size_presets, or part (a part id) to save a part's current fields. A material or finishing operation that isn't in the library is left off with a warnings entry.

POST /a/{team_slug}/estimating/api/v1/products/{id}/used/

Record that the product was picked (it sorts first in the picker).

estimating_products_used · View in API reference

Finishing operations are priced per piece, per 1,000, per sheet, per ft² or m², per linear ft or m of perimeter, each (grommets every so many inches) or per job, with a setup and a minimum. routes limits them to some routes (empty: every route); per-sheet pricing needs sheet or offset. DELETE archives an operation.

POST /a/{team_slug}/estimating/api/v1/finishing/

Finishing operations: priced per piece, per 1,000, per sheet, by area, by perimeter, each or per job.

estimating_finishing_create · View in API reference

Stocks are materials: materials/ also takes runs_on (any, digital, offset), weight_label, weight_gsm, caliper_in, finish, grain, paper_key (the same paper in other sheet sizes), preset_key and board_id (a board of the shared board catalogue, for folding carton and corrugated board). The pricing units follow the material: per sheet and per 1,000 sheets need a sheet size, per 1,000 ft² is for corrugated board and per 1,000 in² for label stock; anything else is 400 on pricing_unit.

Set up a product family#

A team with no presses, machines or rate cards can quote a box at once with a hand price; to price it, one call creates what the family prices with. Nothing is ever created with prices you didn't send.

POST /a/{team_slug}/estimating/api/v1/family-setup/{family}/

Set up quoting for a product family: press, machines by role, the stock, tool rate cards and finishing by preset key, each with any of its fields.

estimating_family_setup · View in API reference

family is carton (the Tuck box: offset press, die-cutter, folder-gluer, board, die rate card, coating) or corrugated (the Shipping box: flexo folder-gluer, corrugated grade, plate rate card, bundling). Every section is optional and takes any field of its row:

Section Row
press The team's default offset press, or a new one (preset_key: start from an offset press preset).
machines By role: die_cutter, folder_gluer, window_patcher, laminator (carton); ffg, die_cutter, folder_gluer, laminator (corrugated). The team's default machine of that kind, or a new one (preset_key: a finishing machine preset of that kind).
stock The stock of the family's product (18pt SBS 28 × 40 in, or C flute 32 ECT kraft made to size), or preset_key: another board preset of the same kind.
tool_rates By tool type: steel_rule_die, foil_block, emboss_die (carton); flexo_plate, rotary_die, steel_rule_die (corrugated).
finishing By finishing preset key, such as finish.aqueous_coating or finish.bundle_strap.
{"estimate": 42,
 "press": {"hourly_rate": "250", "plate_cost": "18", "speed_sph": 10000, "makeready_min_per_pass": 15},
 "machines": {"die_cutter": {"hourly_rate": "160", "speed": 5000, "setup_min": 45},
              "folder_gluer": {"hourly_rate": "120", "speed": 150, "speed_unit": "m_per_min", "setup_min": 30}},
 "stock": {"price_per_unit": "0.60"},
 "tool_rates": {"steel_rule_die": {"base_cost": "250", "cut_cost_per_in": "0.35", "crease_cost_per_in": "0.30"}},
 "finishing": {"finish.aqueous_coating": {"price_per_unit": "0.02"}}}

A row that exists is only filled where it is empty (no value, 0, or a missing key of its settings), so the call can be repeated and never overwrites a rate you set. The answer lists the ids of the rows it made (created), the fields it filled (filled) and every row it touched (rows), and with estimate that estimate's readiness list after the set-up. A refused value is 400 with the errors by section ({"machines": {"die_cutter": {"speed": ["..."]}}}) and nothing is written. Set the new stock on your lines with the parts API. Booklets and labels come in a later release (404 until then).

Poll for the result#

When your customer clicks Finish in the designer, their work is saved, the link's submitted_at and the estimate's designer_submitted_at are set, and the browser goes to return_url (or shows a thank-you message). They can keep editing afterwards; a second Finish updates the times.

GET /a/{team_slug}/estimating/api/v1/estimates/

Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.

estimating_estimates_list · View in API reference

# By your own id
curl -sS -G "$HOST/a/acme/estimating/api/v1/estimates/" -H "$AUTH" --data-urlencode "external_id=order-1001"
# Everything changed since your last poll
curl -sS -G "$HOST/a/acme/estimating/api/v1/estimates/" -H "$AUTH" --data-urlencode "updated_since=2026-09-25T00:00:00Z"
Filter Matches
external_id Exactly.
status draft, sent, won or lost (anything else is 400).
q Text in the name, customer or reference.
updated_since Estimates updated at or after an ISO 8601 date and time (400 if unreadable). URL-encode the + of a time-zone offset.

The list is paged. Poll every 30 to 60 seconds; updated_at also changes on every save while someone is drawing, so wait for designer_submitted_at.

GET /a/{team_slug}/estimating/api/v1/estimates/{id}/

Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.

estimating_estimates_retrieve · View in API reference

The data export#

GET /a/{team_slug}/estimating/api/v1/estimates/{id}/data/

Everything generated for the estimate (schema_version 1): documents, scales, parts with outlines in inches, dimension lines, materials, the quote, production nests with cut-file URLs and designer links.

estimating_estimates_data · View in API reference

One JSON document with everything generated for the estimate. schema_version is 1: fields may be added, and a rename or removal would bump the version. URLs are absolute. Lengths are in inches and areas in square inches; page coordinates are PDF points (or pixels for images) from the top-left corner; money is in US dollars.

Key Contents
estimate The estimate, as in the other calls.
units The unit conventions above.
documents[] Each file with its pages and their sizes.
scales[] Each scale: the line drawn or the ratio, and inches per unit.
parts[] Each part: name, material, quantity, the points as drawn, outline_in, width, height, area and perimeter, and its source, route, inks, sides, finishing, other quantities, description, hand prices and price. Boxes add dieline and dieline_info (null on other parts); every part has family (blank on flat work), spec and plan (null unless the part is priced on its own).
dimension_lines[] Measurement lines and their lengths.
materials[] Every material that is used, offered or the default.
quote Price, cost and margin, per material group (with the nested roll length and cut time) and per part.
production[] Per material group: machine, settings, the nest, cut time, placed pieces and cut_files links.
designer_links[] Every link of the estimate.

Warning: Not for customers as is

The export includes your costs and margins. Don't forward it to customers unchanged.

Changes. Parts sized by hand and price-only items have no graphic: their document, page and points_page are null, a sized part's outline_in is its outline in inches and an item's is null, and kind can be item. If your code reads parts[].document without checking for null, handle it or pass ?sources=drawn (a comma list of drawn, manual, item). The quote always covers every part, so filtered parts may not add up to its total. Quote lines gained the all-in total_price and total_cost; their cost and price are still the material alone.

Nests and cut files#

Groups are named by route and material: roll-7, sheet-34, offset-34. A bare material id (7) still means its roll group. Each roll group of an estimate is nested on its roll, and the quote uses the nested length. Nests run automatically while someone draws. When a group's fresh is false, run one yourself:

POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/nest/

Nest the group now with the Turbo solver (blocks for the time budget) and return the summary with pieces.

estimating_production_nest · View in API reference

With {"optimize": true} it searches for 10 seconds and keeps the current nest unless it finds a shorter one; improved says which. Send revision to get 409 when the drawing changed since you read it.

GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/cutfile/

Cut file of the current (fresh) nest.

estimating_production_cutfile · View in API reference

format is pdf, dxf or cff2; without it you get the cutting machine's own format. pages=beds gives one PDF page per cutter bed. A group whose nest is missing or out of date, or whose material isn't cut, gets 400. Layouts longer than 200 inches carry an X-Cut-File-Warning header.

A planned part (a carton on offset or die-cut, a corrugated box converted on blank, a litho-laminated box) is priced alone in group part-<uuid>. Its summary has key, route, family, material, fresh, status, blocked, notices, needs_rates, plan (ups, sheets, MSF …; for a die-cut box also the die, below), sheet (the cost sheet: material, machine steps, finishing, tooling and outside lines with their cost and price), alternates, compare (the other way of making it, filled by extras/) and equipment. nest/ plans it ({"force": true} plans again); GET quote/ plans the ones without a fresh plan within its budget. PATCH takes equipment ({role: machine id}, null for the kind's default), press, the sheet size, spoilage_pct, makeready_sheets, setup_sheets and plan_overrides (ups; null clears a value). A blocked summary has fix_url, the page where it is fixed. cutfile/ answers 400: a box's die line is its dieline download. save-tool/ adds the die the box's plan prices to the tool library as ordered, with the stations of the plan's nest when its ups come from the nest (see below). Sheet, offset and roll summaries carry finishing_timed ({part id: [{id, machine, units_in, time_s, cost, price, needs_rates}]}) for finishing timed on a machine.

curl -sS -H "$AUTH" -OJ "$HOST/a/acme/estimating/api/v1/estimates/42/production/roll-7/cutfile/?format=dxf"

A die-cut or litho-laminated box's plan names its die: die_type (the dies matched: rotary_die on a rotary die-cutter, else steel_rule_die), die_policy (the one in effect), die_options and die_vs. Each of die_options has key (tool:<id> or new:<ups>), kind (standing or new), tool, tool_number, ups, sheet_in, tier (exact, near or structural; empty for a new die), dist_mm, reasons (why a die isn't used by itself: sheet_differs, not_mountable, not_on_hand, customer_die …), usable, late, ready_on (a new die ordered today, from the rate card's lead time; a die at the die maker, its expected date), customer_owned, and the money at the part's quantity: die_cost / die_price (a die on hand: 0 plus its missing companions, re-rule and wear), line_cost / line_price (everything but the die row), total_cost / total_price (all-in); money is null on a die made for another sheet size when the board doesn't come in that size or the press or die-cutter can't take it, and on a die of more than one up on a made-to-size board (listed, never used). Exactly one option has chosen: true unless the box is blocked (die_sheet_stock: the picked die's sheet size isn't a size of the board, or the press or die-cutter can't take it). die_vs is the cheapest other option that could be used, as key, kind, ups, tool_number and delta_cost / delta_price (its all-in minus the chosen one's), or null. On a die on hand the plan's ups_basis is tool and notices has tool_reused, with die_cheaper_at when a new die would be cheaper all-in at the quantity or an alternate and die_life_exceeded when the quantity or an alternate passes the die's life. A die on hand whose extras need a rate card the team doesn't have (a missing companion, a re-rule) adds tool_rate to needs_rates. Each of alternates includes the die's extras at its own quantity (a re-rule or wear the main quantity doesn't need). Designer links never get plan.

POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/save-tool/

estimating_production_save_tool · View in API reference

GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/

Production summaries for every material group with scaled parts (rows created on demand).

estimating_production_list · View in API reference

PATCH /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/

Change a group's settings.

estimating_production_partial_update · View in API reference

Box dielines#

The box and carton generator has its own endpoints under /a/<team_slug>/dielines/api/v1/. Every call that makes a dieline takes a definition: the style, the three sizes and, optionally, the board and the style's options.

{"definition": {"style": "rte", "L": 2.5, "W": 1.5, "D": 6, "unit": "in", "basis": "inside",
                "board_id": "board.sbs_c1s_18pt", "params": {"tuck_len": 15}}}

GET styles/ lists the styles with their options, ranges and defaults, and GET boards/ lists the boards with your team's board materials (a board material with no catalogue board has board_id null). Option lengths are always millimetres, whatever unit says. A definition may carry allowances: carton_k (0.5 to 2) for carton boards; a_l_mm, a_w_mm and a_d_mm (all three, 0 to 50) and joint_mode for corrugated. Keys left out are the board's.

POST /a/{team_slug}/dielines/api/v1/preview/

Preview a dieline

dielines_preview · View in API reference

A definition with errors gets 400 with {"issues": [...]}: each issue has a code, a severity, the field it is about (L, board_id, params.tuck_len, allowances.carton_k…) and a readable message (every code is listed under Size and option messages). Warnings and notes come back with a successful preview and never block it.

POST /a/{team_slug}/dielines/api/v1/check/

Run the fold check

dielines_check · View in API reference

The fold check folds the box in 3D and looks for clashes. Most sizes answer at once; a slow one answers 202 with {"status": "running", "hash"}, and you poll check/<hash>/ until the report arrives. Results are kept per size, so the same definition is checked once.

POST /a/{team_slug}/dielines/api/v1/export/

Download a dieline file

dielines_export · View in API reference

format is pdf (the dieline for the die maker or cutting table), template (the same lines plus bleed, glue areas and labels, for your designer), dxf, cf2 or svg. The PDF, DXF and CF2 wait for the fold check: a size that fails it gets 409 with {"code": "foldcheck_failed", "report"}, and 503 means the check is still running. The template and the SVG are always available. The same definition always gives the same bytes.

curl -sS -H "$AUTH" -H "Content-Type: application/json" -OJ \
  -d '{"definition": {"style": "fefco_0201", "L": 12, "W": 10, "D": 8, "unit": "in"}, "format": "cf2"}' \
  "$HOST/a/acme/dielines/api/v1/export/"
POST /a/{team_slug}/dielines/api/v1/save/

Save the dieline PDF to Files (an imported single-up: its fold)

dielines_save · View in API reference

Saving stores the dieline PDF in Files with its style, size, board and generator. Send target_file to save a new version of a file you saved before.

POST /a/{team_slug}/dielines/api/v1/open/

Open a dieline saved in Files, or an imported single-up

dielines_open · View in API reference

A single-up from Tools & dies#

An imported single-up dieline (a shape from a die file you imported into Tools & dies) opens in the Box designer read-only. Send {"design": <id>} in place of a definition:

  • open/ returns the read-only payload: the design, its drawing, the export formats and a link to the original file.
  • structure/ folds it in 3D with its saved fold.
  • export/ takes {"design": <id>, "format": "pdf" | "dxf" | "cf2" | "svg"} and returns the stored geometry as it was imported: nothing is regenerated, and there is no artwork template.
  • save/ takes {"design": <id>, "fold": {"angles": {"<crease id>": 90}, "root_panel": "<panel id>"}} and returns {structure, scene}. "fold": null goes back to the default fold.

A fold that names creases the dieline doesn't have gets 400 with {"code": "invalid_fold"}, and a single-up with no panels to fold gets 409 with {"code": "no_structure"}.

Previews allow 600 requests a minute per user; checks, structures, downloads and saves 60.

API operations tagged dielines
MethodPathWhat it does
GET /a/{team_slug}/dielines/api/v1/boards/ List the boards
POST /a/{team_slug}/dielines/api/v1/check/ Run the fold check
GET /a/{team_slug}/dielines/api/v1/check/{digest}/ Poll a running fold check
POST /a/{team_slug}/dielines/api/v1/export/ Download a dieline file
POST /a/{team_slug}/dielines/api/v1/open/ Open a dieline saved in Files, or an imported single-up
POST /a/{team_slug}/dielines/api/v1/preview/ Preview a dieline
POST /a/{team_slug}/dielines/api/v1/save/ Save the dieline PDF to Files (an imported single-up: its fold)
POST /a/{team_slug}/dielines/api/v1/structure/ The 3D structure and scene
GET /a/{team_slug}/dielines/api/v1/styles/ List the box styles
GET /a/{team_slug}/dielines/api/v1/styles/{style_id}/thumbnail.svg A style's thumbnail (SVG)

All “dielines” operations in the API reference

Errors and limits#

Status When
400 Invalid fields, such as metadata that isn't an object or is over 16 KB; a material of another team or archived; a bad file; file or designer_link on PATCH; an unknown status filter.
403 No valid key, not a member of the team, or a designer link doing something its role can't.
404 The estimate, material, link or Files version isn't in this team.
409 external_id already used ({"external_id": [...], "existing_id": 41}), a stale revision, or {"busy": true} while another request is pricing the same group (try again shortly).
429 Too many pricing requests: nest and extras allow 60 a minute per user or designer link.
410 The designer link was revoked or has expired.
413 A JSON body over 5 MB.
507 Your team's Files storage is full: a graphic or document upload is refused (quota_exceeded).
Limit Value
Graphic PDF, PNG, JPG or WebP, up to 50 MB; images up to about 89 megapixels
metadata A JSON object up to 16 KB
external_id 100 characters, unique in the team
material_ids 500 ids
One estimate 2,000 parts, 200 scales, 500 points per polygon, 50,000 drawn or custom outline points in total
A part quantity 1 to 1,000,000; up to 3 other quantities; sizes 0.01 to 2,400 in; up to 12 finishing operations
Parts per call 500 (one-call create, bulk)
Designer link expiry 1 to 365 days, or never
List page 100 estimates

Every estimating endpoint#

Materials (the price list the designer offers) are managed with the materials/ endpoints; deleting one archives it. The takeoff, snap-point and thumbnail endpoints serve the Takeoff workspace itself.

The takeoff sync. PUT takeoff/ replaces the whole drawing: {base_revision, scales, shapes, parts_scope}. With "parts_scope": "all" any part missing from shapes is deleted. Without it, as older integrations call it, only missing drawn parts and dimension lines are deleted: parts sized by hand and price-only items are kept. On a part that already exists, a quoting field you leave out (route, inks, finishing, other quantities, bleed, product, hand prices, shape and size) keeps its value, and null resets it.

API operations tagged estimating
MethodPathWhat it does
GET /a/{team_slug}/estimating/api/v1/designer-links/{token}/ estimating_designer_links_retrieve
PATCH /a/{team_slug}/estimating/api/v1/designer-links/{token}/ estimating_designer_links_partial_update
DELETE /a/{team_slug}/estimating/api/v1/designer-links/{token}/ Revoke the link (sets revoked_at); its URL then answers 410 Gone.
GET /a/{team_slug}/estimating/api/v1/documents/{id}/file/ The document's bytes (the PDF or image as uploaded), inline under its original file name.
GET /a/{team_slug}/estimating/api/v1/documents/{id}/preview/ The document's first-page preview image (404 when it has none).
GET /a/{team_slug}/estimating/api/v1/documents/{id}/snap-points/ Vector vertices of the artwork (page units) for snapping.
GET /a/{team_slug}/estimating/api/v1/estimates/ Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.
POST /a/{team_slug}/estimating/api/v1/estimates/ Create an estimate in one call: fields, optional graphic (`file`), designer materials (`material_ids`, `default_material`) and a designer link (`designer_link`: true or options).
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/ Parts of an estimate: drawn, sized and price-only items.
POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/ Add one sized part or price-only item, at the end of the list.
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/ Parts of an estimate: drawn, sized and price-only items.
PATCH /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/ Change any field; fields left out keep their value.
DELETE /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/ Parts of an estimate: drawn, sized and price-only items.
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/{part_id}/dieline/ Download a box part's dieline
POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/parts/bulk/ Create, update and delete parts in one all-or-nothing call (at most 500 operations).
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/ Production summaries for every material group with scaled parts (rows created on demand).
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/ estimating_production_retrieve
PATCH /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/ Change a group's settings.
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/cutfile/ Cut file of the current (fresh) nest.
POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/extras/ Alternate quantities and route compares of a sheet or offset group (staff): at most 24 re-prices and 8 s per call, `focus` (a part id) first; `pending: true` means call again for the rest.
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/layout/ estimating_production_layout
POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/nest/ Nest the group now with the Turbo solver (blocks for the time budget) and return the summary with pieces.
POST /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/production/{key}/save-tool/ estimating_production_save_tool
GET /a/{team_slug}/estimating/api/v1/estimates/{estimate_pk}/quote/ The estimate's quote: lines, groups and totals, plus valid_until and currency.
GET /a/{team_slug}/estimating/api/v1/estimates/{id}/ Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.
PATCH /a/{team_slug}/estimating/api/v1/estimates/{id}/ Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.
DELETE /a/{team_slug}/estimating/api/v1/estimates/{id}/ Estimates of the team: header fields, integration data (external_id, metadata), the designer's material allowlist, designer links and the full data export.
GET /a/{team_slug}/estimating/api/v1/estimates/{id}/data/ Everything generated for the estimate (schema_version 1): documents, scales, parts with outlines in inches, dimension lines, materials, the quote, production nests with cut-file URLs and designer links.
GET /a/{team_slug}/estimating/api/v1/estimates/{id}/designer-links/ The estimate's designer links, newest first (revoked and expired ones included).
POST /a/{team_slug}/estimating/api/v1/estimates/{id}/designer-links/ Create a designer link: an unguessable URL that opens this estimate's designer without login.
POST /a/{team_slug}/estimating/api/v1/estimates/{id}/documents/ Add a PDF or image to the estimate (same validation as the upload page).
POST /a/{team_slug}/estimating/api/v1/estimates/{id}/documents/from-files/ Add a version from the team's Files library to the estimate: a PDF, PNG, JPG or WebP up to 50 MB.
PUT /a/{team_slug}/estimating/api/v1/estimates/{id}/materials/ Replace the designer's material allowlist ([] = all active materials) and default material.
GET /a/{team_slug}/estimating/api/v1/estimates/{id}/takeoff/ Everything the takeoff workspace needs: documents, scales, shapes, materials and the quote.
PUT /a/{team_slug}/estimating/api/v1/estimates/{id}/takeoff/ Full-state sync: {base_revision, scales, shapes, parts_scope}.
POST /a/{team_slug}/estimating/api/v1/estimates/{id}/thumbnails/ Replace a part's thumbnail (PNG).
POST /a/{team_slug}/estimating/api/v1/family-setup/{family}/ Set up quoting for a product family: press, machines by role, the stock, tool rate cards and finishing by preset key, each with any of its fields.
GET /a/{team_slug}/estimating/api/v1/finishing/ Finishing operations: priced per piece, per 1,000, per sheet, by area, by perimeter, each or per job.
POST /a/{team_slug}/estimating/api/v1/finishing/ Finishing operations: priced per piece, per 1,000, per sheet, by area, by perimeter, each or per job.
GET /a/{team_slug}/estimating/api/v1/finishing/{id}/ Finishing operations: priced per piece, per 1,000, per sheet, by area, by perimeter, each or per job.
PATCH /a/{team_slug}/estimating/api/v1/finishing/{id}/ Finishing operations: priced per piece, per 1,000, per sheet, by area, by perimeter, each or per job.
DELETE /a/{team_slug}/estimating/api/v1/finishing/{id}/ Archives the row.
GET /a/{team_slug}/estimating/api/v1/materials/ estimating_materials_list
POST /a/{team_slug}/estimating/api/v1/materials/ estimating_materials_create
GET /a/{team_slug}/estimating/api/v1/materials/{id}/ estimating_materials_retrieve
PATCH /a/{team_slug}/estimating/api/v1/materials/{id}/ estimating_materials_partial_update
DELETE /a/{team_slug}/estimating/api/v1/materials/{id}/ Archives the material.
GET /a/{team_slug}/estimating/api/v1/products/ Product templates: saved parts with defaults.
POST /a/{team_slug}/estimating/api/v1/products/ Product templates: saved parts with defaults.
GET /a/{team_slug}/estimating/api/v1/products/{id}/ Product templates: saved parts with defaults.
PATCH /a/{team_slug}/estimating/api/v1/products/{id}/ Product templates: saved parts with defaults.
DELETE /a/{team_slug}/estimating/api/v1/products/{id}/ Archives the row.
POST /a/{team_slug}/estimating/api/v1/products/{id}/used/ Record that the product was picked (it sorts first in the picker).

All “estimating” operations in the API reference

Last updated Oct. 1, 2026