Use the existing team estimating API and API keys. Send Authorization: Api-Key $API_KEY from
your server. All examples below use the workspace slug acme. The key's owner must be a member of that workspace.
Keep the key in your integration's secret configuration; never put it in a browser URL.
Create a shell for drawing#
Send JSON to POST /a/acme/estimating/api/v1/estimates/:
{"name": "Postcard A", "customer_name": "C-102", "reference": "PO-5555"}
No file is required. Use the returned id and url, adding #drawing to the URL to open the Drawing tab.
The user signs in normally, uploads artwork with Add file, and measures it. No designer link, external system
linkage or order-volume field is needed. This create call creates a new estimate each time; reference is a label,
not an idempotency key. See create options for the existing optional external_id
uniqueness behavior if your caller needs retry handling.
Get materials only#
GET /a/acme/estimating/api/v1/estimates/42/?output=materials selects material usage. Omit output, or send
output=full, for the existing estimate-detail response.
Example 200 response:
{
"id": 42,
"url": "https://app.example.com/a/acme/estimating/42/",
"name": "Postcard A",
"revision": 3,
"updated_at": "2026-10-20T09:30:00Z",
"last_modified_at": "2026-10-20T09:30:00Z",
"currency": "USD",
"quantity_basis": "estimate",
"status": "ready",
"materials": [{
"material_id": 7, "material": "White film", "external_id": "FILM-01",
"quantity": "10.125", "unit": "ft²", "unit_price": "2.5", "line_total": "25.31",
"usage": "10.125", "usage_unit": "ft²", "qty": 3,
"total_area": "9", "total_area_unit": "ft²"
}],
"csv_columns": {
"parts": ["material", "external_id", "quantity", "unit", "unit_price", "line_total"],
"materials": ["material", "quantity", "unit", "unit_price", "line_total"]
},
"materials_csv": "Material,External id,Quantity,Unit,Unit price,Line total\nWhite film,FILM-01,10.125,ft²,2.5,25.31",
"clipboard_csv": "Material,Quantity,Unit,Unit price,Line total\nWhite film,10.125,ft²,2.5,25.31"
}
materials contains one row per material, including its AutomatePrint material_id and its configured
external_id (which can be empty). Quantities and money are strings to preserve their exported precision.
Quantity uses up to three decimal places; unit prices retain their stored precision; line totals use up to two.
Trailing zeros may be omitted. Currency is currently USD.
JSON rows always include every available material-summary field, regardless of which CSV columns are checked:
usage and usage_unit are aliases of quantity and unit; qty is the total number of parts using the material
(including their quantities); total_area is their net area before waste or nesting, with total_area_unit
(ft² or m², following the estimate's units). Use quantity for material purchasing; qty and total_area
describe the parts, not the material consumed. Price visibility permissions still apply.
csv_columns.parts and csv_columns.materials report the effective saved column selections in export order.
They control CSV layout, not which fields are present in JSON. Part-specific columns such as individual names,
dimensions, scales and notes cannot describe a combined material row; those cells remain blank in materials-only
CSV if selected. Use the parts output for individual part details.
The API and Copy materials only use the same calculation and CSV exporter. Quantity is the total usage for the estimate: nested consumed area where available, otherwise the quote's area including waste or its sheet count. Units match the clipboard. Do not multiply by an order volume again. Cutting, printing, finishing, taxes and quote-level adjustments are not additional material rows.
materials_csv has these fixed English columns:
Material,External id,Quantity,Unit,Unit price,Line total
clipboard_csv contains the same rows, with the workspace's selected clipboard columns and localized headers.
An empty estimate returns an empty materials array and header-only CSV. This output reads saved data; it does not
save another user's browser edits. revision is the existing drawing revision, while last_modified_at covers
other recorded changes too. updated_at remains the estimate header's existing update timestamp.
If a production group still needs pricing, the response is 202 with status: "pending". The other fields keep
the same shape but are provisional: wait for pricing to finish and retry before importing any quantities or
prices. The GET does not start pricing. Responses are private and must not be cached.
Read history and timestamps#
GET /a/acme/estimating/api/v1/estimates/42/history/ returns the normal paginated response (count, next,
previous, results), newest first. Filter with event=changes or event=requested; omit it for both.
/a/{team_slug}/estimating/api/v1/estimates/{id}/history/
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_history ·
View in API reference
Each result contains id, created_at, event (created, changed, deleted, or requested), actor (user ID
or null), actor_label, channel (web, api_key, designer_link, or system), and endpoint (a route name,
not a credential-bearing URL). object_type, object_id, and object_name identify changed records; changes maps field names
to before and after values. Request events carry status_code and output and have no field changes.
Full estimate responses include these read-only summaries:
| Fields | Meaning |
|---|---|
last_modified_at, last_modified_by, last_modified_label |
Latest recorded change, user ID if known, and source label. |
last_requested_at, last_requested_by, last_requested_label |
Latest completed authorized GET made with an API key, the key owner's user ID, and source label. Opening the estimate in the app does not count. |
Timestamp and user-ID values can be null; labels can be blank. GET requests do not move the modification timestamp or drawing revision. The current GET is logged after its response is produced, so its summary describes the previous completed read. History has the same workspace permissions as the estimate. See Estimate history for tracking coverage and the start-of-history limit.
Error handling#
| Status | Meaning |
|---|---|
400 |
Unknown output or history event; the response names the invalid field. |
403 |
Missing, expired or revoked API key, or the key's owner is not a workspace member. |
404 |
Estimate does not exist in the requested workspace. |
500 |
Unexpected export failure. Do not treat the response as CSV or import partial output. |
For example, an invalid selector returns {"output": ["Use full or materials."]}. Other access and subscription
errors use the existing API behavior.