Skip to content
Docs
Sign in

Materials and history API

Create a blank estimate, retrieve materials-only quantities and CSV, and read estimate modification and request history.

For integrators

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.

GET /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.

Last updated Oct. 20, 2026